From 653ddc1d184b173698a4cf6ba780bdeada81bd60 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Oct 2021 04:19:59 +0900 Subject: [PATCH 1/4] sd-dhcp6-client: introduce sd_dhcp6_lease_get_timestamp() --- src/libsystemd-network/dhcp6-lease-internal.h | 1 + src/libsystemd-network/dhcp6-network.c | 4 ++ src/libsystemd-network/sd-dhcp6-client.c | 50 +++++++++++++++---- src/libsystemd-network/sd-dhcp6-lease.c | 13 +++++ src/systemd/sd-dhcp6-lease.h | 3 ++ 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 8801497b722..66af241df53 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -18,6 +18,7 @@ struct sd_dhcp6_lease { size_t serverid_len; uint8_t preference; bool rapid_commit; + triple_timestamp timestamp; DHCP6IA ia; DHCP6IA pd; diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index 4f7bd53de4e..eedd92d3c24 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -47,6 +47,10 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { if (r < 0) return r; + r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true); + if (r < 0) + return r; + r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) return -errno; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index aacdf069807..9a6be647897 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -21,6 +21,7 @@ #include "hexdecoct.h" #include "hostname-util.h" #include "in-addr-util.h" +#include "io-util.h" #include "network-common.h" #include "random-util.h" #include "socket-util.h" @@ -1341,13 +1342,14 @@ static int client_parse_message( return 0; } -static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { +static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len, const triple_timestamp *t) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; bool rapid_commit; int r; assert(client); assert(reply); + assert(t); if (reply->type != DHCP6_MESSAGE_REPLY) return 0; @@ -1356,6 +1358,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si if (r < 0) return -ENOMEM; + lease->timestamp = *t; + r = client_parse_message(client, reply, len, lease); if (r < 0) return r; @@ -1375,11 +1379,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si return DHCP6_STATE_BOUND; } -static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) { +static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len, const triple_timestamp *t) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; uint8_t pref_advertise = 0, pref_lease = 0; int r; + assert(client); + assert(advertise); + assert(t); + if (advertise->type != DHCP6_MESSAGE_ADVERTISE) return 0; @@ -1387,6 +1395,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver if (r < 0) return r; + lease->timestamp = *t; + r = client_parse_message(client, advertise, len, lease); if (r < 0) return r; @@ -1417,6 +1427,17 @@ static int client_receive_message( sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); + /* This needs to be initialized with zero. See #20741. */ + CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {}; + struct iovec iov; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + triple_timestamp t = {}; _cleanup_free_ DHCP6Message *message = NULL; ssize_t buflen, len; int r = 0; @@ -1427,9 +1448,8 @@ static int client_receive_message( buflen = next_datagram_size_fd(fd); if (buflen == -ENETDOWN) - /* the link is down. Don't return an error or the I/O event - source will be disconnected and we won't be able to receive - packets again when the link comes back. */ + /* the link is down. Don't return an error or the I/O event source will be disconnected + * and we won't be able to receive packets again when the link comes back. */ return 0; if (buflen < 0) return buflen; @@ -1438,7 +1458,9 @@ static int client_receive_message( if (!message) return -ENOMEM; - len = recv(fd, message, buflen, 0); + iov = IOVEC_MAKE(message, buflen); + + len = recvmsg_safe(fd, &msg, MSG_DONTWAIT); if (len < 0) { /* see comment above for why we shouldn't error out on ENETDOWN. */ if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) @@ -1452,6 +1474,16 @@ static int client_receive_message( return 0; } + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + } + + if (!triple_timestamp_is_set(&t)) + triple_timestamp_get(&t); + if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { const char *type_str = dhcp6_message_type_to_string(message->type); if (type_str) @@ -1466,7 +1498,7 @@ static int client_receive_message( switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - r = client_receive_reply(client, message, len); + r = client_receive_reply(client, message, len, &t); if (r < 0) { log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); return 0; @@ -1479,7 +1511,7 @@ static int client_receive_message( break; case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len); + r = client_receive_advertise(client, message, len, &t); if (r < 0) { log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); return 0; @@ -1495,7 +1527,7 @@ static int client_receive_message( case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: - r = client_receive_reply(client, message, len); + r = client_receive_reply(client, message, len, &t); if (r < 0) { log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); return 0; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index c55b06d2f75..6c7ffc7999c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -11,6 +11,19 @@ #include "strv.h" #include "util.h" +int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { + assert_return(lease, -EINVAL); + assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP); + assert_return(clock_supported(clock), -EOPNOTSUPP); + assert_return(ret, -EINVAL); + + if (!triple_timestamp_is_set(&lease->timestamp)) + return -ENODATA; + + *ret = triple_timestamp_by_clock(&lease->timestamp, clock); + return 0; +} + int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { DHCP6Address *addr; uint32_t valid = 0, t; diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index a10901e5cb9..04c6383051f 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -21,6 +21,7 @@ #include #include +#include #include "_sd-common.h" @@ -28,6 +29,8 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp6_lease sd_dhcp6_lease; +int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); + void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, From 937e894747ef2c852a5488221db33c3bf1b36786 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Oct 2021 04:21:21 +0900 Subject: [PATCH 2/4] network: dhcp6pd: set lifetime to routes for assigned prefixes Note that the kernel (at least 5.14.11) seems not to support lifetime for IPv6 unreachable routes. The lifetime for routes of the type will be handled by sd-event's timer event source. So, we cannot confirm the lifetime with 'ip route' command. --- src/network/networkd-dhcp6.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 6ec178f3540..0795ebdd467 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -288,7 +288,7 @@ static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix) { +static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, usec_t timestamp_usec, uint32_t lifetime_sec) { _cleanup_(route_freep) Route *route = NULL; Route *existing; int r; @@ -310,6 +310,7 @@ static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix) { route->dst_prefixlen = 64; route->protocol = RTPROT_DHCP; route->priority = link->network->dhcp6_pd_route_metric; + route->lifetime = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); if (route_get(NULL, link, route, &existing) < 0) link->dhcp6_pd_configured = false; @@ -420,6 +421,7 @@ static int dhcp6_pd_request_address( static int dhcp6_pd_assign_prefix( Link *link, const struct in6_addr *prefix, + usec_t timestamp_usec, uint32_t lifetime_preferred, uint32_t lifetime_valid) { @@ -435,7 +437,7 @@ static int dhcp6_pd_assign_prefix( return r; } - r = dhcp6_pd_request_route(link, prefix); + r = dhcp6_pd_request_route(link, prefix, timestamp_usec, lifetime_valid); if (r < 0) return r; @@ -527,6 +529,7 @@ static int dhcp6_pd_prefix_distribute( Link *dhcp6_link, const struct in6_addr *pd_prefix, uint8_t pd_prefix_len, + usec_t timestamp_usec, uint32_t lifetime_preferred, uint32_t lifetime_valid, bool assign_preferred_subnet_id) { @@ -560,7 +563,7 @@ static int dhcp6_pd_prefix_distribute( continue; (void) in6_addr_prefix_to_string(&assigned_prefix, 64, &buf); - r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred, lifetime_valid); + r = dhcp6_pd_assign_prefix(link, &assigned_prefix, timestamp_usec, lifetime_preferred, lifetime_valid); if (r < 0) { log_link_warning_errno(link, r, "Failed to assign/update prefix %s: %m", strna(buf)); if (link == dhcp6_link) @@ -746,7 +749,7 @@ static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li return 1; } -static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen) { +static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen, usec_t timestamp_usec, uint32_t lifetime_sec) { _cleanup_(route_freep) Route *route = NULL; _cleanup_free_ char *buf = NULL; Route *existing; @@ -774,6 +777,7 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad route->table = link_get_dhcp6_route_table(link); route->type = RTN_UNREACHABLE; route->protocol = RTPROT_DHCP; + route->lifetime = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); if (route_get(link->manager, NULL, route, &existing) < 0) link->dhcp6_configured = false; @@ -826,12 +830,17 @@ static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_ } static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { + usec_t timestamp_usec; Link *link; int r; assert(dhcp6_link); assert(dhcp6_link->dhcp6_lease); + r = sd_dhcp6_lease_get_timestamp(dhcp6_link->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + if (r < 0) + return log_link_warning_errno(dhcp6_link, r, "Failed to get timestamp of DHCPv6 lease: %m"); + HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) { if (link == dhcp6_link) continue; @@ -857,7 +866,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { if (r == 0) continue; - r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len); + r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len, timestamp_usec, lifetime_valid); if (r < 0) return r; @@ -889,6 +898,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { r = dhcp6_pd_prefix_distribute(dhcp6_link, &pd_prefix, pd_prefix_len, + timestamp_usec, lifetime_preferred, lifetime_valid, true); @@ -898,6 +908,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) { r = dhcp6_pd_prefix_distribute(dhcp6_link, &pd_prefix, pd_prefix_len, + timestamp_usec, lifetime_preferred, lifetime_valid, false); From 10ab5ed5a859e0c7a8f43fd80611ee5efe6bd61d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Oct 2021 06:00:22 +0900 Subject: [PATCH 3/4] network: dhcp6: explicitly specify metric for unreachable route For static IPv6 routes without metric is specified, then we use 1024. But such an adjustment is not performed to dynamic routes. So, let's specify the metric explicitly. Otherwise, configured routes will be handled as foreign. --- src/network/networkd-dhcp6.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0795ebdd467..2bbad4edbbc 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -777,6 +777,7 @@ static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *ad route->table = link_get_dhcp6_route_table(link); route->type = RTN_UNREACHABLE; route->protocol = RTPROT_DHCP; + route->priority = DHCP_ROUTE_METRIC; route->lifetime = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC); if (route_get(link->manager, NULL, route, &existing) < 0) From d0619f2c2b3865e930da9832a246c97d35f63b85 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 15 Oct 2021 05:57:30 +0900 Subject: [PATCH 4/4] network: dhcp6pd: set default metric 256 for delegated prefix When Assign= in [DHCPv6PrefixDelegation] is enabled, then the kernel will create the prefix route for the assigned address with metric 256. When Assign= is disabled, then the kernel will create the route with metric 1024. For the default value, we should choose a smaller value (higher priority) than 1024, as the unreachable routes for delegated prefix will be configured with 1024. --- man/systemd.network.xml | 3 ++- src/network/networkd-dhcp-common.h | 1 + src/network/networkd-network.c | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index bcc86417c69..01297c397f3 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -2162,7 +2162,8 @@ Table=1234 RouteMetric= The metric of the route to the delegated prefix subnet. Takes an unsigned integer in - the range 0…4294967295. When unset or set to 0, the kernel's default value is used. + the range 0…4294967295. When set to 0, the kernel's default value is used. Defaults to 256. + diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 4a98038391a..2573247db20 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -10,6 +10,7 @@ #include "time-util.h" #define DHCP_ROUTE_METRIC 1024 +#define DHCP6PD_ROUTE_METRIC 256 typedef struct Link Link; typedef struct Manager Manager; diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index a367041e991..798f4099297 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -409,7 +409,8 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_pd_announce = true, .dhcp6_pd_assign = true, .dhcp6_pd_manage_temporary_address = true, - .dhcp6_pd_subnet_id = -1, + .dhcp6_pd_subnet_id = UINT64_MAX, + .dhcp6_pd_route_metric = DHCP6PD_ROUTE_METRIC, .dhcp_server_bind_to_interface = true, .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,