]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ndisc-option: allow to set valid time of options
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 17 Mar 2024 06:23:38 +0000 (15:23 +0900)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 28 Mar 2024 12:34:09 +0000 (12:34 +0000)
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.

src/libsystemd-network/fuzz-ndisc-rs.c
src/libsystemd-network/ndisc-option.c
src/libsystemd-network/ndisc-option.h
src/libsystemd-network/sd-ndisc.c

index eed670fb6f017180c09784f02be677aa3ecdfa39..8a720e628e9afb6f150cb1aae16bf0078c8a1c0e 100644 (file)
@@ -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);
index 855ec0469a70f9096aa8f4828e73afea7766ea33..5a3847686ace8beb37aa6c92247f397c25223301 100644 (file)
@@ -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:
index 3bf29af0c96c92b5edc2744b0e68f60d93c4876f..da52858caa0eb728165131552508e0591a1a4653 100644 (file)
@@ -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);
index 4dbf1b95d6e17f161e93cb87de11202a1baa4888..18752a94e7af9698b20b4344cd81c52083254723 100644 (file)
@@ -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) {