From: Yu Watanabe Date: Sun, 17 Mar 2024 06:23:38 +0000 (+0900) Subject: ndisc-option: allow to set valid time of options X-Git-Tag: v256-rc1~367 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ff944339f32034e42d79a78e91d495f1e415affb;p=thirdparty%2Fsystemd.git ndisc-option: allow to set valid time of options When an option is delegated from an upstream server, its lifetime may be limited by a time. Such functionality will be used later by sd-radv. This also remove 'offset' argument from the option setter ndisc_option_set_xyz(), and make it update existing option. See comments in ndisc_option_add_link_layer_address() for more details. --- diff --git a/src/libsystemd-network/fuzz-ndisc-rs.c b/src/libsystemd-network/fuzz-ndisc-rs.c index eed670fb6f0..8a720e628e9 100644 --- a/src/libsystemd-network/fuzz-ndisc-rs.c +++ b/src/libsystemd-network/fuzz-ndisc-rs.c @@ -53,7 +53,7 @@ static void test_with_icmp6_packet(const uint8_t *data, size_t size) { if (ndisc_parse_options(packet, &options) < 0) return; - if (ndisc_send(fd_pair[1], &dst, icmp6_packet_get_header(packet), options) < 0) + if (ndisc_send(fd_pair[1], &dst, icmp6_packet_get_header(packet), options, /* timestamp = */ 0) < 0) return; packet = icmp6_packet_unref(packet); diff --git a/src/libsystemd-network/ndisc-option.c b/src/libsystemd-network/ndisc-option.c index 855ec0469a7..5a3847686ac 100644 --- a/src/libsystemd-network/ndisc-option.c +++ b/src/libsystemd-network/ndisc-option.c @@ -212,6 +212,9 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( ndisc_option_free); static int ndisc_option_consume(Set **options, sd_ndisc_option *p) { + assert(options); + assert(p); + if (set_size(*options) >= MAX_OPTIONS) { ndisc_option_free(p); return -ETOOMANYREFS; /* recognizable error code */ @@ -220,7 +223,7 @@ static int ndisc_option_consume(Set **options, sd_ndisc_option *p) { return set_ensure_consume(options, &ndisc_option_hash_ops, p); } -int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint8_t *bytes) { +int ndisc_option_set_raw(Set **options, size_t length, const uint8_t *bytes) { _cleanup_free_ uint8_t *copy = NULL; assert(options); @@ -233,7 +236,7 @@ int ndisc_option_add_raw(Set **options, size_t offset, size_t length, const uint if (!copy) return -ENOMEM; - sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, offset); + sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, /* offset = */ 0); if (!p) return -ENOMEM; @@ -258,15 +261,29 @@ static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret) return 0; } -int ndisc_option_add_link_layer_address(Set **options, uint8_t opt, size_t offset, const struct ether_addr *mac) { +int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) { assert(options); - assert(IN_SET(opt, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS)); - assert(mac); + assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS)); - if (ether_addr_is_null(mac)) - return -EINVAL; + if (!mac || ether_addr_is_null(mac)) { + ndisc_option_remove_by_type(*options, type); + return 0; + } + + sd_ndisc_option *p = ndisc_option_get_by_type(*options, type); + if (p) { + /* offset == 0 means that we are now building a packet to be sent, and in that case we allow + * to override the option we previously set. + * offset != 0 means that we are now parsing a received packet, and we refuse to override + * conflicting options. */ + if (offset != 0) + return -EEXIST; - sd_ndisc_option *p = ndisc_option_new(opt, offset); + p->mac = *mac; + return 0; + } + + p = ndisc_option_new(type, offset); if (!p) return -ENOMEM; @@ -288,6 +305,9 @@ static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, s struct ether_addr mac; memcpy(&mac, opt + 2, sizeof(struct ether_addr)); + if (ether_addr_is_null(&mac)) + return -EBADMSG; + return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac); } @@ -310,14 +330,16 @@ static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option, return 0; } -int ndisc_option_add_prefix( +int ndisc_option_add_prefix_internal( Set **options, size_t offset, uint8_t flags, uint8_t prefixlen, const struct in6_addr *address, usec_t valid_lifetime, - usec_t preferred_lifetime) { + usec_t preferred_lifetime, + usec_t valid_until, + usec_t preferred_until) { assert(options); assert(address); @@ -325,26 +347,51 @@ int ndisc_option_add_prefix( if (prefixlen > 128) return -EINVAL; - if (in6_addr_is_link_local(address)) + struct in6_addr addr = *address; + in6_addr_mask(&addr, prefixlen); + + if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr)) return -EINVAL; if (preferred_lifetime > valid_lifetime) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset); + if (preferred_until > valid_until) + return -EINVAL; + + sd_ndisc_option *p = ndisc_option_get( + *options, + &(const sd_ndisc_option) { + .type = SD_NDISC_OPTION_PREFIX_INFORMATION, + .prefix.prefixlen = prefixlen, + .prefix.address = addr, + }); + if (p) { + if (offset != 0) + return -EEXIST; + + p->prefix.flags = flags; + p->prefix.valid_lifetime = valid_lifetime; + p->prefix.preferred_lifetime = preferred_lifetime; + p->prefix.valid_until = valid_until; + p->prefix.preferred_until = preferred_until; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset); if (!p) return -ENOMEM; p->prefix = (sd_ndisc_prefix) { .flags = flags, .prefixlen = prefixlen, - .address = *address, + .address = addr, .valid_lifetime = valid_lifetime, .preferred_lifetime = preferred_lifetime, + .valid_until = valid_until, + .preferred_until = preferred_until, }; - in6_addr_mask(&p->prefix.address, p->prefix.prefixlen); - return ndisc_option_consume(options, p); } @@ -367,10 +414,12 @@ static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, c if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64) flags &= ~ND_OPT_PI_FLAG_AUTO; - return ndisc_option_add_prefix(options, offset, flags, pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, valid, pref); + return ndisc_option_add_prefix(options, offset, flags, + pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix, + valid, pref); } -static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **ret) { +static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { assert(option); assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION); assert(ret); @@ -381,13 +430,19 @@ static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **re if (!buf) return -ENOMEM; + usec_t valid = MIN(option->prefix.valid_lifetime, + usec_sub_unsigned(option->prefix.valid_until, timestamp)); + usec_t pref = MIN3(valid, + option->prefix.preferred_lifetime, + usec_sub_unsigned(option->prefix.preferred_until, timestamp)); + *buf = (struct nd_opt_prefix_info) { .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION, .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8, .nd_opt_pi_prefix_len = option->prefix.prefixlen, .nd_opt_pi_flags_reserved = option->prefix.flags, - .nd_opt_pi_valid_time = usec_to_be32_sec(option->prefix.valid_lifetime), - .nd_opt_pi_preferred_time = usec_to_be32_sec(option->prefix.preferred_lifetime), + .nd_opt_pi_valid_time = usec_to_be32_sec(valid), + .nd_opt_pi_preferred_time = usec_to_be32_sec(pref), .nd_opt_pi_prefix = option->prefix.address, }; @@ -397,9 +452,22 @@ static int ndisc_option_build_prefix(const sd_ndisc_option *option, uint8_t **re int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) { assert(options); - assert(hdr); - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset); + if (!hdr) { + ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER); + return 0; + } + + sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER); + if (p) { + if (offset != 0) + return -EEXIST; + + memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr)); + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset); if (!p) return -ENOMEM; @@ -454,7 +522,16 @@ int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) { if (mtu < IPV6_MIN_MTU) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset); + sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU); + if (p) { + if (offset != 0) + return -EEXIST; + + p->mtu = mtu; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset); if (!p) return -ENOMEM; @@ -498,19 +575,39 @@ static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret) return 0; } -int ndisc_option_add_home_agent(Set **options, size_t offset, uint16_t preference, usec_t lifetime) { +int ndisc_option_add_home_agent_internal( + Set **options, + size_t offset, + uint16_t preference, + usec_t lifetime, + usec_t valid_until) { + assert(options); if (lifetime > UINT16_MAX * USEC_PER_SEC) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset); + sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT); + if (p) { + if (offset != 0) + return -EEXIST; + + p->home_agent = (sd_ndisc_home_agent) { + .preference = preference, + .lifetime = lifetime, + .valid_until = valid_until, + }; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset); if (!p) return -ENOMEM; p->home_agent = (sd_ndisc_home_agent) { .preference = preference, .lifetime = lifetime, + .valid_until = valid_until, }; return ndisc_option_consume(options, p); @@ -533,13 +630,16 @@ static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t le be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity = */ false)); } -static int ndisc_option_build_home_agent(const sd_ndisc_option *option, uint8_t **ret) { +static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { assert(option); assert(option->type == SD_NDISC_OPTION_HOME_AGENT); assert(ret); assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0); + usec_t lifetime = MIN(option->home_agent.lifetime, + usec_sub_unsigned(option->home_agent.valid_until, timestamp)); + _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1); if (!buf) return -ENOMEM; @@ -548,20 +648,21 @@ static int ndisc_option_build_home_agent(const sd_ndisc_option *option, uint8_t .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT, .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8, .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference), - .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(option->home_agent.lifetime), + .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime), }; *ret = (uint8_t*) TAKE_PTR(buf); return 0; } -int ndisc_option_add_route( +int ndisc_option_add_route_internal( Set **options, size_t offset, uint8_t preference, uint8_t prefixlen, const struct in6_addr *prefix, - usec_t lifetime) { + usec_t lifetime, + usec_t valid_until) { assert(options); assert(prefix); @@ -577,19 +678,38 @@ int ndisc_option_add_route( if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH)) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset); + struct in6_addr addr = *prefix; + in6_addr_mask(&addr, prefixlen); + + sd_ndisc_option *p = ndisc_option_get( + *options, + &(const sd_ndisc_option) { + .type = SD_NDISC_OPTION_ROUTE_INFORMATION, + .route.prefixlen = prefixlen, + .route.address = addr, + }); + if (p) { + if (offset != 0) + return -EEXIST; + + p->route.preference = preference; + p->route.lifetime = lifetime; + p->route.valid_until = valid_until; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset); if (!p) return -ENOMEM; p->route = (sd_ndisc_route) { .preference = preference, .prefixlen = prefixlen, - .address = *prefix, + .address = addr, .lifetime = lifetime, + .valid_until = valid_until, }; - in6_addr_mask(&p->route.address, p->route.prefixlen); - return ndisc_option_consume(options, p); } @@ -620,14 +740,15 @@ static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, co return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime); } -static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret) { +static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { assert(option); assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION); assert(option->route.prefixlen <= 128); assert(ret); size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64); - be32_t lifetime = usec_to_be32_sec(option->route.lifetime); + be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime, + usec_sub_unsigned(option->route.valid_until, timestamp))); _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8); if (!buf) @@ -644,12 +765,13 @@ static int ndisc_option_build_route(const sd_ndisc_option *option, uint8_t **ret return 0; } -int ndisc_option_add_rdnss( +int ndisc_option_add_rdnss_internal( Set **options, size_t offset, size_t n_addresses, const struct in6_addr *addresses, - usec_t lifetime) { + usec_t lifetime, + usec_t valid_until) { assert(options); assert(addresses); @@ -669,6 +791,7 @@ int ndisc_option_add_rdnss( .n_addresses = n_addresses, .addresses = TAKE_PTR(addrs), .lifetime = lifetime, + .valid_until = valid_until, }; return ndisc_option_consume(options, p); @@ -690,13 +813,14 @@ static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, co return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime); } -static int ndisc_option_build_rdnss(const sd_ndisc_option *option, uint8_t **ret) { +static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { assert(option); assert(option->type == SD_NDISC_OPTION_RDNSS); assert(ret); size_t len = option->rdnss.n_addresses * 2 + 1; - be32_t lifetime = usec_to_be32_sec(option->rdnss.lifetime); + be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime, + usec_sub_unsigned(option->rdnss.valid_until, timestamp))); _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8); if (!buf) @@ -719,7 +843,16 @@ int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flag if ((flags & UINT64_C(0x00ffffffffffff00)) != flags) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset); + sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION); + if (p) { + if (offset != 0) + return -EEXIST; + + p->extended_flags = flags; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset); if (!p) return -ENOMEM; @@ -759,7 +892,13 @@ static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uin return 0; } -int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, usec_t lifetime) { +int ndisc_option_add_dnssl_internal( + Set **options, + size_t offset, char * + const *domains, + usec_t lifetime, + usec_t valid_until) { + int r; assert(options); @@ -787,6 +926,7 @@ int ndisc_option_add_dnssl(Set **options, size_t offset, char * const *domains, p->dnssl = (sd_ndisc_dnssl) { .domains = TAKE_PTR(copy), .lifetime = lifetime, + .valid_until = valid_until, }; return ndisc_option_consume(options, p); @@ -863,7 +1003,7 @@ static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, co return ndisc_option_add_dnssl(options, offset, l, lifetime); } -static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret) { + static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { int r; assert(option); @@ -875,7 +1015,8 @@ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, uint8_t **ret len += strlen(*s) + 2; len = DIV_ROUND_UP(len, 8); - be32_t lifetime = usec_to_be32_sec(option->dnssl.lifetime); + be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime, + usec_sub_unsigned(option->dnssl.valid_until, timestamp))); _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8); if (!buf) @@ -916,11 +1057,19 @@ int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *po if (!in_charset(portal, URI_VALID)) return -EINVAL; + sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL); + if (p) { + if (offset != 0) + return -EEXIST; + + return free_and_strdup(&p->captive_portal, portal); + } + _cleanup_free_ char *copy = strdup(portal); if (!copy) return -ENOMEM; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset); + p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset); if (!p) return -ENOMEM; @@ -1011,12 +1160,13 @@ static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret return 0; } -int ndisc_option_add_prefix64( +int ndisc_option_add_prefix64_internal( Set **options, size_t offset, uint8_t prefixlen, const struct in6_addr *prefix, - usec_t lifetime) { + usec_t lifetime, + usec_t valid_until) { int r; @@ -1030,18 +1180,36 @@ int ndisc_option_add_prefix64( if (lifetime > PREF64_MAX_LIFETIME_USEC) return -EINVAL; - sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset); + struct in6_addr addr = *prefix; + in6_addr_mask(&addr, prefixlen); + + sd_ndisc_option *p = ndisc_option_get( + *options, + &(const sd_ndisc_option) { + .type = SD_NDISC_OPTION_PREF64, + .prefix64.prefixlen = prefixlen, + .prefix64.prefix = addr, + }); + if (p) { + if (offset != 0) + return -EEXIST; + + p->prefix64.lifetime = lifetime; + p->prefix64.valid_until = valid_until; + return 0; + } + + p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset); if (!p) return -ENOMEM; p->prefix64 = (sd_ndisc_prefix64) { .prefixlen = prefixlen, - .prefix = *prefix, + .prefix = addr, .lifetime = lifetime, + .valid_until = valid_until, }; - in6_addr_mask(&p->prefix64.prefix, p->prefix64.prefixlen); - return ndisc_option_consume(options, p); } @@ -1070,7 +1238,7 @@ static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len, return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime); } -static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t **ret) { +static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) { int r; assert(option); @@ -1082,11 +1250,9 @@ static int ndisc_option_build_prefix64(const sd_ndisc_option *option, uint8_t ** if (r < 0) return r; - uint16_t lifetime; - if (option->prefix64.lifetime >= PREF64_SCALED_LIFETIME_MASK * USEC_PER_SEC) - lifetime = PREF64_SCALED_LIFETIME_MASK; - else - lifetime = (uint16_t) DIV_ROUND_UP(option->prefix64.lifetime, USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK; + uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime, + usec_sub_unsigned(option->prefix64.valid_until, timestamp)), + USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK; _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8); if (!buf) @@ -1232,7 +1398,7 @@ int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) { return 0; } -int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options) { +int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) { int r; assert(fd >= 0); @@ -1279,7 +1445,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h break; case SD_NDISC_OPTION_PREFIX_INFORMATION: - r = ndisc_option_build_prefix(option, &buf); + r = ndisc_option_build_prefix(option, timestamp, &buf); break; case SD_NDISC_OPTION_REDIRECTED_HEADER: @@ -1291,15 +1457,15 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h break; case SD_NDISC_OPTION_HOME_AGENT: - r = ndisc_option_build_home_agent(option, &buf); + r = ndisc_option_build_home_agent(option, timestamp, &buf); break; case SD_NDISC_OPTION_ROUTE_INFORMATION: - r = ndisc_option_build_route(option, &buf); + r = ndisc_option_build_route(option, timestamp, &buf); break; case SD_NDISC_OPTION_RDNSS: - r = ndisc_option_build_rdnss(option, &buf); + r = ndisc_option_build_rdnss(option, timestamp, &buf); break; case SD_NDISC_OPTION_FLAGS_EXTENSION: @@ -1307,7 +1473,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h break; case SD_NDISC_OPTION_DNSSL: - r = ndisc_option_build_dnssl(option, &buf); + r = ndisc_option_build_dnssl(option, timestamp, &buf); break; case SD_NDISC_OPTION_CAPTIVE_PORTAL: @@ -1315,7 +1481,7 @@ int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *h break; case SD_NDISC_OPTION_PREF64: - r = ndisc_option_build_prefix64(option, &buf); + r = ndisc_option_build_prefix64(option, timestamp, &buf); break; default: diff --git a/src/libsystemd-network/ndisc-option.h b/src/libsystemd-network/ndisc-option.h index 3bf29af0c96..da52858caa0 100644 --- a/src/libsystemd-network/ndisc-option.h +++ b/src/libsystemd-network/ndisc-option.h @@ -27,11 +27,15 @@ typedef struct sd_ndisc_prefix { struct in6_addr address; usec_t valid_lifetime; usec_t preferred_lifetime; + /* timestamp in CLOCK_BOOTTIME, used when sending option for adjusting lifetime. */ + usec_t valid_until; + usec_t preferred_until; } sd_ndisc_prefix; typedef struct sd_ndisc_home_agent { uint16_t preference; usec_t lifetime; + usec_t valid_until; } sd_ndisc_home_agent; typedef struct sd_ndisc_route { @@ -39,23 +43,27 @@ typedef struct sd_ndisc_route { uint8_t prefixlen; struct in6_addr address; usec_t lifetime; + usec_t valid_until; } sd_ndisc_route; typedef struct sd_ndisc_rdnss { size_t n_addresses; struct in6_addr *addresses; usec_t lifetime; + usec_t valid_until; } sd_ndisc_rdnss; typedef struct sd_ndisc_dnssl { char **domains; usec_t lifetime; + usec_t valid_until; } sd_ndisc_dnssl; typedef struct sd_ndisc_prefix64 { uint8_t prefixlen; struct in6_addr prefix; usec_t lifetime; + usec_t valid_until; } sd_ndisc_prefix64; typedef struct sd_ndisc_option { @@ -130,24 +138,56 @@ static inline void ndisc_option_remove_by_type(Set *options, uint8_t type) { ndisc_option_remove(options, &(const sd_ndisc_option) { .type = type }); } -int ndisc_option_add_raw( +int ndisc_option_set_raw( Set **options, - size_t offset, size_t length, const uint8_t *bytes); int ndisc_option_add_link_layer_address( Set **options, - uint8_t opt, + uint8_t type, size_t offset, const struct ether_addr *mac); -int ndisc_option_add_prefix( +static inline int ndisc_option_set_link_layer_address( + Set **options, + uint8_t type, + const struct ether_addr *mac) { + return ndisc_option_add_link_layer_address(options, type, 0, mac); +} +int ndisc_option_add_prefix_internal( + Set **options, + size_t offset, + uint8_t flags, + uint8_t prefixlen, + const struct in6_addr *address, + usec_t valid_lifetime, + usec_t preferred_lifetime, + usec_t valid_until, + usec_t preferred_until); +static inline int ndisc_option_add_prefix( Set **options, size_t offset, uint8_t flags, uint8_t prefixlen, const struct in6_addr *address, usec_t valid_lifetime, - usec_t preferred_lifetime); + usec_t preferred_lifetime) { + return ndisc_option_add_prefix_internal(options, offset, flags, prefixlen, address, + valid_lifetime, preferred_lifetime, + USEC_INFINITY, USEC_INFINITY); +} +static inline int ndisc_option_set_prefix( + Set **options, + uint8_t flags, + uint8_t prefixlen, + const struct in6_addr *address, + usec_t valid_lifetime, + usec_t preferred_lifetime, + usec_t valid_until, + usec_t preferred_until) { + return ndisc_option_add_prefix_internal(options, 0, flags, prefixlen, address, + valid_lifetime, preferred_lifetime, + valid_until, preferred_until); +} int ndisc_option_add_redirected_header( Set **options, size_t offset, @@ -156,42 +196,135 @@ int ndisc_option_add_mtu( Set **options, size_t offset, uint32_t mtu); -int ndisc_option_add_home_agent( +static inline int ndisc_option_set_mtu( + Set **options, + uint32_t mtu) { + return ndisc_option_add_mtu(options, 0, mtu); +} +int ndisc_option_add_home_agent_internal( + Set **options, + size_t offset, + uint16_t preference, + usec_t lifetime, + usec_t valid_until); +static inline int ndisc_option_add_home_agent( Set **options, size_t offset, uint16_t preference, - usec_t lifetime); -int ndisc_option_add_route( + usec_t lifetime) { + return ndisc_option_add_home_agent_internal(options, offset, preference, lifetime, USEC_INFINITY); +} +static inline int ndisc_option_set_home_agent( + Set **options, + uint16_t preference, + usec_t lifetime, + usec_t valid_until) { + return ndisc_option_add_home_agent_internal(options, 0, preference, lifetime, valid_until); +} +int ndisc_option_add_route_internal( Set **options, size_t offset, uint8_t preference, uint8_t prefixlen, const struct in6_addr *prefix, - usec_t lifetime); -int ndisc_option_add_rdnss( + usec_t lifetime, + usec_t valid_until); +static inline int ndisc_option_add_route( + Set **options, + size_t offset, + uint8_t preference, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime) { + return ndisc_option_add_route_internal(options, offset, preference, prefixlen, prefix, lifetime, USEC_INFINITY); +} +static inline int ndisc_option_set_route( + Set **options, + uint8_t preference, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime, + usec_t valid_until) { + return ndisc_option_add_route_internal(options, 0, preference, prefixlen, prefix, lifetime, valid_until); +} +int ndisc_option_add_rdnss_internal( + Set **options, + size_t offset, + size_t n_addresses, + const struct in6_addr *addresses, + usec_t lifetime, + usec_t valid_until); +static inline int ndisc_option_add_rdnss( Set **options, size_t offset, size_t n_addresses, const struct in6_addr *addresses, - usec_t lifetime); + usec_t lifetime) { + return ndisc_option_add_rdnss_internal(options, offset, n_addresses, addresses, lifetime, USEC_INFINITY); +} +static inline int ndisc_option_set_rdnss( + Set **options, + size_t n_addresses, + const struct in6_addr *addresses, + usec_t lifetime, + usec_t valid_until) { + return ndisc_option_add_rdnss_internal(options, 0, n_addresses, addresses, lifetime, valid_until); +} int ndisc_option_add_flags_extension( Set **options, size_t offset, uint64_t flags); -int ndisc_option_add_dnssl( +int ndisc_option_add_dnssl_internal( + Set **options, + size_t offset, + char * const *domains, + usec_t lifetime, + usec_t valid_until); +static inline int ndisc_option_add_dnssl( Set **options, size_t offset, char * const *domains, - usec_t lifetime); + usec_t lifetime) { + return ndisc_option_add_dnssl_internal(options, offset, domains, lifetime, USEC_INFINITY); +} +static inline int ndisc_option_set_dnssl( + Set **options, + char * const *domains, + usec_t lifetime, + usec_t valid_until) { + return ndisc_option_add_dnssl_internal(options, 0, domains, lifetime, valid_until); +} int ndisc_option_add_captive_portal( Set **options, size_t offset, const char *portal); -int ndisc_option_add_prefix64( +static inline int ndisc_option_set_captive_portal( + Set **options, + const char *portal) { + return ndisc_option_add_captive_portal(options, 0, portal); +} +int ndisc_option_add_prefix64_internal( Set **options, size_t offset, uint8_t prefixlen, const struct in6_addr *prefix, - usec_t lifetime); + usec_t lifetime, + usec_t valid_until); +static inline int ndisc_option_add_prefix64( + Set **options, + size_t offset, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime) { + return ndisc_option_add_prefix64_internal(options, offset, prefixlen, prefix, lifetime, USEC_INFINITY); +} +static inline int ndisc_option_set_prefix64( + Set **options, + uint8_t prefixlen, + const struct in6_addr *prefix, + usec_t lifetime, + usec_t valid_until) { + return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until); +} -int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options); +int ndisc_send(int fd, const struct sockaddr_in6 *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp); diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 4dbf1b95d6e..18752a94e7a 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -284,12 +284,12 @@ static int ndisc_send_router_solicitation(sd_ndisc *nd) { assert(nd); if (!ether_addr_is_null(&nd->mac_addr)) { - r = ndisc_option_add_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, 0, &nd->mac_addr); + r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &nd->mac_addr); if (r < 0) return r; } - return ndisc_send(nd->fd, &dst, &header.nd_rs_hdr, options); + return ndisc_send(nd->fd, &dst, &header.nd_rs_hdr, options, USEC_INFINITY); } static usec_t ndisc_timeout_compute_random(usec_t val) {