summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-05-12 12:12:22 +0200
committerGitHub <noreply@github.com>2021-05-12 12:12:22 +0200
commit7a7e58ce443221ed09a9bb15dcef84fdbc5574a0 (patch)
treecc1e4df7b702d7130fd824310f7be58b4dc0bb2e
parent8f7123731d2a269ee9985cc265b6e69af63c1b6b (diff)
parent932e157b5e0936a5f531b8bb559997830eb14739 (diff)
downloadsystemd-7a7e58ce443221ed09a9bb15dcef84fdbc5574a0.tar.gz
Merge pull request #19533 from yuwata/network-queue
network: introduce queue to configure address, route, etc
-rw-r--r--src/basic/hashmap.h30
-rw-r--r--src/basic/in-addr-util.h13
-rw-r--r--src/basic/ordered-set.h11
-rw-r--r--src/basic/set.h13
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.c40
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h8
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/networkd-address.c371
-rw-r--r--src/network/networkd-address.h23
-rw-r--r--src/network/networkd-dhcp4.c410
-rw-r--r--src/network/networkd-dhcp6.c333
-rw-r--r--src/network/networkd-dhcp6.h2
-rw-r--r--src/network/networkd-ipv4ll.c72
-rw-r--r--src/network/networkd-link.c100
-rw-r--r--src/network/networkd-link.h27
-rw-r--r--src/network/networkd-manager.c7
-rw-r--r--src/network/networkd-manager.h5
-rw-r--r--src/network/networkd-ndisc.c296
-rw-r--r--src/network/networkd-neighbor.c141
-rw-r--r--src/network/networkd-neighbor.h11
-rw-r--r--src/network/networkd-network.h2
-rw-r--r--src/network/networkd-nexthop.c239
-rw-r--r--src/network/networkd-nexthop.h5
-rw-r--r--src/network/networkd-queue.c151
-rw-r--r--src/network/networkd-queue.h60
-rw-r--r--src/network/networkd-route.c549
-rw-r--r--src/network/networkd-route.h21
-rw-r--r--src/network/networkd-routing-policy-rule.c318
-rw-r--r--src/network/networkd-routing-policy-rule.h8
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py12
30 files changed, 2124 insertions, 1156 deletions
diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h
index c20ee8eb4b..c855f39b1d 100644
--- a/src/basic/hashmap.h
+++ b/src/basic/hashmap.h
@@ -371,28 +371,26 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
return _hashmap_first_key(HASHMAP_BASE(h), false);
}
-#define hashmap_clear_with_destructor(_s, _f) \
+#define hashmap_clear_with_destructor(h, f) \
({ \
+ Hashmap *_h = (h); \
void *_item; \
- while ((_item = hashmap_steal_first(_s))) \
- _f(_item); \
+ while ((_item = hashmap_steal_first(_h))) \
+ f(_item); \
+ _h; \
})
-#define hashmap_free_with_destructor(_s, _f) \
- ({ \
- hashmap_clear_with_destructor(_s, _f); \
- hashmap_free(_s); \
- })
-#define ordered_hashmap_clear_with_destructor(_s, _f) \
+#define hashmap_free_with_destructor(h, f) \
+ hashmap_free(hashmap_clear_with_destructor(h, f))
+#define ordered_hashmap_clear_with_destructor(h, f) \
({ \
+ OrderedHashmap *_h = (h); \
void *_item; \
- while ((_item = ordered_hashmap_steal_first(_s))) \
- _f(_item); \
- })
-#define ordered_hashmap_free_with_destructor(_s, _f) \
- ({ \
- ordered_hashmap_clear_with_destructor(_s, _f); \
- ordered_hashmap_free(_s); \
+ while ((_item = ordered_hashmap_steal_first(_h))) \
+ f(_item); \
+ _h; \
})
+#define ordered_hashmap_free_with_destructor(h, f) \
+ ordered_hashmap_free(ordered_hashmap_clear_with_destructor(h, f))
/* no hashmap_next */
void* ordered_hashmap_next(OrderedHashmap *h, const void *key);
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index d5caf662ab..906b3fe97e 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -73,7 +73,13 @@ int in_addr_prefix_range(
union in_addr_union *ret_start,
union in_addr_union *ret_end);
int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
+static inline int in6_addr_to_string(const struct in6_addr *u, char **ret) {
+ return in_addr_to_string(AF_INET6, (const union in_addr_union*) u, ret);
+}
int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
+static inline int in6_addr_prefix_to_string(const struct in6_addr *u, unsigned prefixlen, char **ret) {
+ return in_addr_prefix_to_string(AF_INET6, (const union in_addr_union*) u, prefixlen, ret);
+}
int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
static inline int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) {
return in_addr_port_ifindex_name_to_string(family, u, 0, ifindex, NULL, ret);
@@ -120,3 +126,10 @@ extern const struct hash_ops in_addr_data_hash_ops;
extern const struct hash_ops in_addr_prefix_hash_ops;
extern const struct hash_ops in_addr_prefix_hash_ops_free;
extern const struct hash_ops in6_addr_hash_ops;
+
+#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
+#define IPV4_ADDRESS_FMT_VAL(address) \
+ be32toh((address).s_addr) >> 24, \
+ (be32toh((address).s_addr) >> 16) & 0xFFu, \
+ (be32toh((address).s_addr) >> 8) & 0xFFu, \
+ be32toh((address).s_addr) & 0xFFu
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
index a377f20b1f..64df41766f 100644
--- a/src/basic/ordered-set.h
+++ b/src/basic/ordered-set.h
@@ -75,6 +75,17 @@ void ordered_set_print(FILE *f, const char *field, OrderedSet *s);
#define ORDERED_SET_FOREACH(e, s) \
_ORDERED_SET_FOREACH(e, s, UNIQ_T(i, UNIQ))
+#define ordered_set_clear_with_destructor(s, f) \
+ ({ \
+ OrderedSet *_s = (s); \
+ void *_item; \
+ while ((_item = ordered_set_steal_first(_s))) \
+ f(_item); \
+ _s; \
+ })
+#define ordered_set_free_with_destructor(s, f) \
+ ordered_set_free(ordered_set_clear_with_destructor(s, f))
+
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
diff --git a/src/basic/set.h b/src/basic/set.h
index 52b6f4984c..0f8673934f 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -95,17 +95,16 @@ static inline void *set_steal_first(Set *s) {
return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
}
-#define set_clear_with_destructor(_s, _f) \
+#define set_clear_with_destructor(s, f) \
({ \
+ Set *_s = (s); \
void *_item; \
while ((_item = set_steal_first(_s))) \
- _f(_item); \
- })
-#define set_free_with_destructor(_s, _f) \
- ({ \
- set_clear_with_destructor(_s, _f); \
- set_free(_s); \
+ f(_item); \
+ _s; \
})
+#define set_free_with_destructor(s, f) \
+ set_free(set_clear_with_destructor(s, f))
/* no set_steal_first_key */
/* no set_first_key */
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
index 448c50d4ab..90a184319a 100644
--- a/src/libsystemd/sd-netlink/netlink-util.c
+++ b/src/libsystemd/sd-netlink/netlink-util.c
@@ -409,6 +409,44 @@ int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void
return 0;
}
+MultipathRoute *multipath_route_free(MultipathRoute *m) {
+ if (!m)
+ return NULL;
+
+ free(m->ifname);
+
+ return mfree(m);
+}
+
+int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) {
+ _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
+ _cleanup_free_ char *ifname = NULL;
+
+ assert(m);
+ assert(ret);
+
+ if (m->ifname) {
+ ifname = strdup(m->ifname);
+ if (!ifname)
+ return -ENOMEM;
+ }
+
+ n = new(MultipathRoute, 1);
+ if (!n)
+ return -ENOMEM;
+
+ *n = (MultipathRoute) {
+ .gateway = m->gateway,
+ .weight = m->weight,
+ .ifindex = m->ifindex,
+ .ifname = TAKE_PTR(ifname),
+ };
+
+ *ret = TAKE_PTR(n);
+
+ return 0;
+}
+
int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) {
_cleanup_ordered_set_free_free_ OrderedSet *set = NULL;
int r;
@@ -420,7 +458,7 @@ int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, O
return -EBADMSG;
for (; size >= sizeof(struct rtnexthop); ) {
- _cleanup_free_ MultipathRoute *m = NULL;
+ _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
return -EBADMSG;
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index a3a3951ff7..98bbfa4513 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -19,10 +19,16 @@ typedef struct RouteVia {
typedef struct MultipathRoute {
RouteVia gateway;
- int ifindex;
uint32_t weight;
+ int ifindex;
+ char *ifname;
} MultipathRoute;
+MultipathRoute *multipath_route_free(MultipathRoute *m);
+DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free);
+
+int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
+
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
uint32_t rtnl_message_get_serial(sd_netlink_message *m);
void rtnl_message_seal(sd_netlink_message *m);
diff --git a/src/network/meson.build b/src/network/meson.build
index 4fca3106dc..a8b9232e64 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -105,6 +105,8 @@ sources = files('''
networkd-network.h
networkd-nexthop.c
networkd-nexthop.h
+ networkd-queue.c
+ networkd-queue.h
networkd-route.c
networkd-route.h
networkd-routing-policy-rule.c
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 724b0f95d3..5a3388c3c4 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -9,9 +9,9 @@
#include "netlink-util.h"
#include "networkd-address-pool.h"
#include "networkd-address.h"
-#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "networkd-queue.h"
#include "parse-util.h"
#include "string-util.h"
#include "strv.h"
@@ -150,7 +150,7 @@ static bool address_may_have_broadcast(const Address *a) {
* See https://tools.ietf.org/html/rfc3021 */
return a->family == AF_INET &&
- in_addr_is_null(AF_INET, &a->in_addr_peer) &&
+ in4_addr_is_null(&a->in_addr_peer.in) &&
a->prefixlen <= 30;
}
@@ -406,7 +406,7 @@ static int address_update(Address *address, const Address *src) {
}
if (address->family == AF_INET6 &&
- in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
+ in6_addr_is_link_local(&address->in_addr.in6) > 0 &&
in6_addr_is_null(&address->link->ipv6ll_address)) {
r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
@@ -485,6 +485,63 @@ int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
return address_get(link, a, NULL) >= 0;
}
+static int link_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
+ Address *a;
+
+ assert(address);
+
+ SET_FOREACH(a, addresses) {
+ if (a->family != AF_INET)
+ continue;
+
+ if (!in4_addr_equal(&a->in_addr.in, address))
+ continue;
+
+ if (ret)
+ *ret = a;
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
+ Link *link;
+ int r;
+
+ assert(manager);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(address);
+
+ if (family == AF_INET)
+ HASHMAP_FOREACH(link, manager->links) {
+ Address *a;
+
+ if (link_get_ipv4_address(link->addresses, &address->in, &a) >= 0)
+ return !check_ready || address_is_ready(a);
+ if (link_get_ipv4_address(link->addresses_foreign, &address->in, &a) >= 0)
+ return !check_ready || address_is_ready(a);
+ }
+ else {
+ _cleanup_(address_freep) Address *tmp = NULL;
+ Address *a;
+
+ r = address_new(&tmp);
+ if (r < 0)
+ return r;
+
+ tmp->family = family;
+ tmp->in_addr = *address;
+
+ HASHMAP_FOREACH(link, manager->links)
+ if (address_get(link, tmp, &a) >= 0)
+ return !check_ready || address_is_ready(a);
+ }
+
+ return false;
+}
+
static void log_address_debug(const Address *address, const char *str, const Link *link) {
_cleanup_free_ char *addr = NULL, *peer = NULL;
char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX];
@@ -520,25 +577,6 @@ static void log_address_debug(const Address *address, const char *str, const Lin
preferred_str ? "for " : "forever", strempty(preferred_str));
}
-static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(m);
- assert(link);
- assert(link->ifname);
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_message_warning_errno(link, m, r, "Could not drop address");
- else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
-
- return 1;
-}
-
static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) {
int r;
@@ -570,6 +608,33 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
return 0;
}
+int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
+ int r;
+
+ assert(rtnl);
+ assert(m);
+ assert(link);
+ assert(link->address_remove_messages > 0);
+ assert(error_msg);
+
+ link->address_remove_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EADDRNOTAVAIL)
+ log_link_message_warning_errno(link, m, r, error_msg);
+ else if (r >= 0)
+ (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+ return 1;
+}
+
+static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ return address_remove_handler_internal(rtnl, m, link, "Could not drop address");
+}
+
int address_remove(
const Address *address,
Link *link,
@@ -603,6 +668,7 @@ int address_remove(
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
+ link->address_remove_messages++;
return 0;
}
@@ -706,7 +772,7 @@ int link_drop_foreign_addresses(Link *link) {
SET_FOREACH(address, link->addresses_foreign) {
/* we consider IPv6LL addresses to be managed by the kernel */
- if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
+ if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link))
continue;
if (link_address_is_dynamic(link, address)) {
@@ -732,35 +798,6 @@ int link_drop_foreign_addresses(Link *link) {
return r;
}
-static int remove_static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(m);
- assert(link);
- assert(link->ifname);
- assert(link->address_remove_messages > 0);
-
- link->address_remove_messages--;
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_message_warning_errno(link, m, r, "Could not drop address");
- else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
-
- if (link->address_remove_messages == 0 && link->request_static_addresses) {
- link_set_state(link, LINK_STATE_CONFIGURING);
- r = link_set_addresses(link);
- if (r < 0)
- link_enter_failed(link);
- }
-
- return 1;
-}
-
int link_drop_addresses(Link *link) {
Address *address, *pool_address;
int k, r = 0;
@@ -769,17 +806,15 @@ int link_drop_addresses(Link *link) {
SET_FOREACH(address, link->addresses) {
/* we consider IPv6LL addresses to be managed by the kernel */
- if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))
+ if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link))
continue;
- k = address_remove(address, link, remove_static_address_handler);
+ k = address_remove(address, link, NULL);
if (k < 0 && r >= 0) {
r = k;
continue;
}
- link->address_remove_messages++;
-
SET_FOREACH(pool_address, link->pool_addresses)
if (address_equal(address, pool_address))
address_free(set_remove(link->pool_addresses, pool_address));
@@ -845,9 +880,31 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
return 1;
}
+int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
+ int r;
+
+ assert(rtnl);
+ assert(m);
+ assert(link);
+ assert(error_msg);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_warning_errno(link, m, r, error_msg);
+ link_enter_failed(link);
+ return 0;
+ } else if (r >= 0)
+ (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+ return 1;
+}
+
static int ipv4_dad_configure(Address *address);
-int address_configure(
+static int address_configure(
const Address *address,
Link *link,
link_netlink_message_handler_t callback,
@@ -951,78 +1008,24 @@ int address_configure(
return k;
}
-static int static_address_ready_callback(Address *address) {
- Address *a;
- Link *link;
- int r;
-
- assert(address);
- assert(address->link);
-
- link = address->link;
-
- if (!link->addresses_configured)
- return 0;
-
- SET_FOREACH(a, link->static_addresses)
- if (!address_is_ready(a)) {
- _cleanup_free_ char *str = NULL;
-
- (void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str);
- log_link_debug(link, "an address %s is not ready", strnull(str));
- return 0;
- }
-
- /* This should not be called again */
- SET_FOREACH(a, link->static_addresses)
- a->callback = NULL;
-
- link->addresses_ready = true;
-
- r = link_set_ipv6_proxy_ndp_addresses(link);
- if (r < 0)
- return r;
-
- return link_set_routes(link);
-}
-
-static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
- assert(rtnl);
- assert(m);
assert(link);
- assert(link->ifname);
- assert(link->address_messages > 0);
-
- link->address_messages--;
+ assert(link->static_address_messages > 0);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
+ link->static_address_messages--;
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
-
- if (link->address_messages == 0) {
- Address *a;
+ r = address_configure_handler_internal(rtnl, m, link, "Failed to set static address");
+ if (r <= 0)
+ return r;
+ if (link->static_address_messages == 0) {
log_link_debug(link, "Addresses set");
- link->addresses_configured = true;
-
- /* When all static addresses are already ready, then static_address_ready_callback()
- * will not be called automatically. So, call it here. */
- a = set_first(link->static_addresses);
- if (!a) {
- log_link_debug(link, "No static address is stored. Already removed?");
- return 1;
- }
+ link->static_addresses_configured = true;
+ link_check_ready(link);
- r = static_address_ready_callback(a);
+ r = dhcp4_server_configure(link);
if (r < 0)
link_enter_failed(link);
}
@@ -1030,55 +1033,65 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
return 1;
}
-static int static_address_configure(const Address *address, Link *link) {
- Address *ret;
+static int static_address_after_configure(Request *req, void *object) {
+ Address *address = object;
+ Link *link;
int r;
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
assert(address);
- assert(link);
-
- r = address_configure(address, link, address_handler, &ret);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not configure static address: %m");
- link->address_messages++;
+ link = req->link;
- r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret);
+ r = set_ensure_put(&link->static_addresses, &address_hash_ops, address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to store static address: %m");
- ret->callback = static_address_ready_callback;
-
return 0;
}
-int link_set_addresses(Link *link) {
- Address *ad;
+int link_request_address(
+ Link *link,
+ Address *address,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ assert(link);
+ assert(address);
+
+ log_address_debug(address, "Requesting", link);
+ return link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
+ message_counter, netlink_handler, ret);
+}
+
+int link_request_static_addresses(Link *link) {
+ Address *a;
Prefix *p;
int r;
assert(link);
assert(link->network);
- if (link->address_remove_messages != 0) {
- log_link_debug(link, "Removing old addresses, new addresses will be configured later.");
- link->request_static_addresses = true;
- return 0;
- }
+ link->static_addresses_configured = false;
- if (link->address_messages != 0) {
- log_link_debug(link, "Static addresses are configuring.");
- return 0;
- }
+ ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) {
+ Request *req;
- ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
- r = static_address_configure(ad, link);
+ r = link_request_address(link, a, false, &link->static_address_messages,
+ static_address_handler, &req);
if (r < 0)
return r;
+
+ req->after_configure = static_address_after_configure;
}
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
_cleanup_(address_freep) Address *address = NULL;
+ Request *req;
if (!p->assign)
continue;
@@ -1097,22 +1110,18 @@ int link_set_addresses(Link *link) {
address->family = AF_INET6;
address->route_metric = p->route_metric;
- r = static_address_configure(address, link);
- if (r < 0)
- return r;
- }
-
- if (link->address_messages == 0) {
- link->addresses_configured = true;
- link->addresses_ready = true;
- r = link_set_ipv6_proxy_ndp_addresses(link);
+ r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
+ static_address_handler, &req);
if (r < 0)
return r;
- r = link_set_routes(link);
- if (r < 0)
- return r;
+ req->after_configure = static_address_after_configure;
+ }
+
+ if (link->static_address_messages == 0) {
+ link->static_addresses_configured = true;
+ link_check_ready(link);
} else {
log_link_debug(link, "Setting addresses");
link_set_state(link, LINK_STATE_CONFIGURING);
@@ -1121,6 +1130,34 @@ int link_set_addresses(Link *link) {
return 0;
}
+int request_process_address(Request *req) {
+ Address *ret = NULL; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->address);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
+
+ if (!link_is_ready_to_configure(req->link, false))
+ return 0;
+
+ if (req->link->address_remove_messages > 0)
+ return 0;
+
+ r = address_configure(req->address, req->link, req->netlink_handler, &ret);
+ if (r < 0)
+ return r;
+
+ if (req->after_configure) {
+ r = req->after_configure(req, ret);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(address_freep) Address *tmp = NULL;
Link *link = NULL;
@@ -1304,7 +1341,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
}
static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
- _cleanup_free_ char *pretty = NULL;
Address *address;
Link *link;
int r;
@@ -1315,22 +1351,26 @@ static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
address = (Address *) userdata;
link = address->link;
- (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
+ assert(address->family == AF_INET);
+
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
log_link_debug(link, "Stopping ACD client...");
return;
case SD_IPV4ACD_EVENT_BIND:
- log_link_debug(link, "Successfully claimed address %s", strna(pretty));
+ log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
link_check_ready(link);
break;
case SD_IPV4ACD_EVENT_CONFLICT:
- log_link_warning(link, "DAD conflict. Dropping address %s", strna(pretty));
+ log_link_warning(link, "DAD conflict. Dropping address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link, NULL);
if (r < 0)
- log_link_error_errno(link, r, "Failed to drop DAD conflicted address %s", strna(pretty));;
+ log_link_error_errno(link, r, "Failed to drop DAD conflicted address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
link_check_ready(link);
break;
@@ -1353,12 +1393,7 @@ static int ipv4_dad_configure(Address *address) {
if (address->family != AF_INET)
return 0;
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *pretty = NULL;
-
- (void) in_addr_to_string(address->family, &address->in_addr, &pretty);
- log_link_debug(address->link, "Starting IPv4ACD client. Probing address %s", strna(pretty));
- }
+ log_address_debug(address, "Starting IPv4ACD client. Probing", address->link);
if (!address->acd) {
r = sd_ipv4acd_new(&address->acd);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 58f44cd4cd..e9fddddf9f 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -16,6 +16,7 @@
typedef struct Manager Manager;
typedef struct Network Network;
+typedef struct Request Request;
typedef int (*address_ready_callback_t)(Address *address);
typedef struct Address {
@@ -51,7 +52,8 @@ typedef struct Address {
int address_new(Address **ret);
Address *address_free(Address *address);
int address_get(Link *link, const Address *in, Address **ret);
-int address_configure(const Address *address, Link *link, link_netlink_message_handler_t callback, Address **ret);
+int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
+int address_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
int address_remove(const Address *address, Link *link, link_netlink_message_handler_t callback);
bool address_equal(const Address *a1, const Address *a2);
bool address_is_ready(const Address *a);
@@ -60,16 +62,26 @@ int generate_ipv6_eui_64_address(const Link *link, struct in6_addr *ret);
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
-int link_set_addresses(Link *link);
int link_drop_addresses(Link *link);
int link_drop_foreign_addresses(Link *link);
bool link_address_is_dynamic(const Link *link, const Address *address);
int link_has_ipv6_address(Link *link, const struct in6_addr *address);
+int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
void ipv4_dad_unref(Link *link);
int ipv4_dad_stop(Link *link);
int ipv4_dad_update_mac(Link *link);
+int link_request_address(
+ Link *link,
+ Address *address,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret);
+int link_request_static_addresses(Link *link);
+int request_process_address(Request *req);
+
int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m);
void network_drop_invalid_addresses(Network *network);
@@ -86,10 +98,3 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
CONFIG_PARSER_PROTOTYPE(config_parse_address_route_metric);
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
-
-#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
-#define IPV4_ADDRESS_FMT_VAL(address) \
- be32toh((address).s_addr) >> 24, \
- (be32toh((address).s_addr) >> 16) & 0xFFu, \
- (be32toh((address).s_addr) >> 8) & 0xFFu, \
- be32toh((address).s_addr) & 0xFFu
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index f20264be9e..abb37a3d26 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -17,13 +17,16 @@
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "networkd-nexthop.h"
+#include "networkd-queue.h"
+#include "networkd-route.h"
#include "networkd-state-file.h"
#include "string-table.h"
#include "strv.h"
#include "sysctl-util.h"
#include "web-util.h"
-static int dhcp4_update_address(Link *link, bool announce);
+static int dhcp4_request_address_and_routes(Link *link, bool announce);
static int dhcp4_remove_all(Link *link);
void network_adjust_dhcp4(Network *network) {
@@ -83,8 +86,20 @@ static void dhcp4_check_ready(Link *link) {
if (link->network->dhcp_send_decline && !link->dhcp4_address_bind)
return;
- if (link->dhcp4_messages > 0)
+ if (link->dhcp4_messages > 0) {
+ log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__);
return;
+ }
+
+ if (!link->dhcp_address) {
+ log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__);
+ return;
+ }
+
+ if (!address_is_ready(link->dhcp_address)) {
+ log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__);
+ return;
+ }
link->dhcp4_configured = true;
@@ -102,6 +117,27 @@ static void dhcp4_check_ready(Link *link) {
link_check_ready(link);
}
+static int dhcp4_after_route_configure(Request *req, void *object) {
+ Route *route = object;
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ROUTE);
+ assert(route);
+
+ link = req->link;
+
+ r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
+
+ set_remove(link->dhcp_routes_old, route);
+
+ return 0;
+}
+
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -135,6 +171,15 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
r = dhcp4_remove_all(link);
if (r < 0)
link_enter_failed(link);
+
+ r = link_request_static_nexthops(link, true);
+ if (r < 0)
+ link_enter_failed(link);
+
+ r = link_request_static_routes(link, true);
+ if (r < 0)
+ link_enter_failed(link);
+
return 1;
}
@@ -143,24 +188,26 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
return 1;
}
-static int dhcp_route_configure(Route *route, Link *link) {
- Route *ret;
+static int dhcp4_request_route(Route *in, Link *link) {
+ _cleanup_(route_freep) Route *route = in;
+ Request *req;
int r;
assert(route);
assert(link);
- r = route_configure(route, link, dhcp4_route_handler, &ret);
+ r = link_has_route(link, route);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m");
-
- link->dhcp4_messages++;
+ return r;
+ if (r == 0)
+ link->dhcp4_configured = false;
- r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret);
+ r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
+ dhcp4_route_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
+ return r;
- (void) set_remove(link->dhcp_routes_old, ret);
+ req->after_configure = dhcp4_after_route_configure;
return 0;
}
@@ -171,7 +218,7 @@ static bool link_prefixroute(Link *link) {
link->manager->dhcp4_prefix_root_cannot_set_table;
}
-static int link_set_dhcp_prefix_route(Link *link) {
+static int dhcp4_request_prefix_route(Link *link) {
_cleanup_(route_freep) Route *route = NULL;
struct in_addr address, netmask;
int r;
@@ -204,10 +251,10 @@ static int link_set_dhcp_prefix_route(Link *link) {
route->table = link_get_dhcp_route_table(link);
route->mtu = link->network->dhcp_route_mtu;
- return dhcp_route_configure(route, link);
+ return dhcp4_request_route(TAKE_PTR(route), link);
}
-static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw) {
_cleanup_(route_freep) Route *route = NULL;
struct in_addr address;
int r;
@@ -234,14 +281,15 @@ static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw)
route->table = link_get_dhcp_route_table(link);
route->mtu = link->network->dhcp_route_mtu;
- return dhcp_route_configure(route, link);
+ return dhcp4_request_route(TAKE_PTR(route), link);
}
-static int dhcp_route_configure_auto(
- Route *route,
+static int dhcp4_request_route_auto(
+ Route *in,
Link *link,
const struct in_addr *gw) {
+ _cleanup_(route_freep) Route *route = in;
struct in_addr address, netmask, prefix;
uint8_t prefixlen;
int r;
@@ -251,8 +299,6 @@ static int dhcp_route_configure_auto(
assert(link->dhcp_lease);
assert(gw);
- /* The route object may be reused in an iteration. All elements must be set or cleared. */
-
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0)
return r;
@@ -309,7 +355,7 @@ static int dhcp_route_configure_auto(
return 0;
}
- r = link_set_dhcp_route_to_gateway(link, gw);
+ r = dhcp4_request_route_to_gateway(link, gw);
if (r < 0)
return r;
@@ -319,13 +365,12 @@ static int dhcp_route_configure_auto(
route->prefsrc.in = address;
}
- return dhcp_route_configure(route, link);
+ return dhcp4_request_route(TAKE_PTR(route), link);
}
-static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_gw) {
+static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_gw) {
_cleanup_free_ sd_dhcp_route **static_routes = NULL;
bool classless_route = false, static_route = false;
- _cleanup_(route_freep) Route *route = NULL;
struct in_addr default_gw = {};
int n, r;
@@ -360,24 +405,25 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g
if (classless_route && static_route)
log_link_debug(link, "Classless static routes received from DHCP server: ignoring static-route option");
- r = route_new(&route);
- if (r < 0)
- return r;
-
- route->family = AF_INET;
- route->gw_family = AF_INET;
- route->protocol = RTPROT_DHCP;
- route->priority = link->network->dhcp_route_metric;
- route->table = link_get_dhcp_route_table(link);
- route->mtu = link->network->dhcp_route_mtu;
-
for (int i = 0; i < n; i++) {
+ _cleanup_(route_freep) Route *route = NULL;
struct in_addr gw;
if (sd_dhcp_route_get_option(static_routes[i]) !=
(classless_route ? SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE : SD_DHCP_OPTION_STATIC_ROUTE))
continue;
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
+ route->gw_family = AF_INET;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+ route->mtu = link->network->dhcp_route_mtu;
+
r = sd_dhcp_route_get_gateway(static_routes[i], &gw);
if (r < 0)
return r;
@@ -398,7 +444,7 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g
in4_addr_is_null(&default_gw))
default_gw = gw;
- r = dhcp_route_configure_auto(route, link, &gw);
+ r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
if (r < 0)
return r;
}
@@ -407,7 +453,7 @@ static int link_set_dhcp_static_routes(Link *link, struct in_addr *ret_default_g
return classless_route;
}
-static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) {
+static int dhcp4_request_gateway(Link *link, struct in_addr *ret_gw) {
_cleanup_(route_freep) Route *route = NULL;
const struct in_addr *router;
struct in_addr address;
@@ -439,7 +485,7 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) {
/* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host
* so that we can route no matter the netmask or existing kernel route tables. */
- r = link_set_dhcp_route_to_gateway(link, &router[0]);
+ r = dhcp4_request_route_to_gateway(link, &router[0]);
if (r < 0)
return r;
@@ -457,7 +503,7 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) {
route->table = link_get_dhcp_route_table(link);
route->mtu = link->network->dhcp_route_mtu;
- r = dhcp_route_configure(route, link);
+ r = dhcp4_request_route(TAKE_PTR(route), link);
if (r < 0)
return r;
@@ -468,17 +514,21 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) {
if (rt->gw_family != AF_INET)
continue;
- rt->gw.in = router[0];
- if (!rt->protocol_set)
- rt->protocol = RTPROT_DHCP;
- if (!rt->priority_set)
- rt->priority = link->network->dhcp_route_metric;
- if (!rt->table_set)
- rt->table = link_get_dhcp_route_table(link);
- if (rt->mtu == 0)
- rt->mtu = link->network->dhcp_route_mtu;
-
- r = dhcp_route_configure(rt, link);
+ r = route_dup(rt, &route);
+ if (r < 0)
+ return r;
+
+ route->gw.in = router[0];
+ if (!route->protocol_set)
+ route->protocol = RTPROT_DHCP;
+ if (!route->priority_set)
+ route->priority = link->network->dhcp_route_metric;
+ if (!route->table_set)
+ route->table = link_get_dhcp_route_table(link);
+ if (route->mtu == 0)
+ route->mtu = link->network->dhcp_route_mtu;
+
+ r = dhcp4_request_route(TAKE_PTR(route), link);
if (r < 0)
return r;
}
@@ -487,13 +537,12 @@ static int link_set_dhcp_gateway(Link *link, struct in_addr *ret_gw) {
return 0;
}
-static int link_set_routes_to_servers(
+static int dhcp4_request_routes_to_servers(
Link *link,
const struct in_addr *servers,
size_t n_servers,
const struct in_addr *gw) {
- _cleanup_(route_freep) Route *route = NULL;
int r;
assert(link);
@@ -502,24 +551,25 @@ static int link_set_routes_to_servers(
assert(servers || n_servers == 0);
assert(gw);
- r = route_new(&route);
- if (r < 0)
- return r;
-
- route->family = AF_INET;
- route->dst_prefixlen = 32;
- route->protocol = RTPROT_DHCP;
- route->priority = link->network->dhcp_route_metric;
- route->table = link_get_dhcp_route_table(link);
- route->mtu = link->network->dhcp_route_mtu;
-
for (size_t i = 0; i < n_servers; i++) {
+ _cleanup_(route_freep) Route *route = NULL;
+
if (in4_addr_is_null(&servers[i]))
continue;
+ r = route_new(&route);
+ if (r < 0)
+ return r;
+
+ route->family = AF_INET;
route->dst.in = servers[i];
+ route->dst_prefixlen = 32;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = link_get_dhcp_route_table(link);
+ route->mtu = link->network->dhcp_route_mtu;
- r = dhcp_route_configure_auto(route, link, gw);
+ r = dhcp4_request_route_auto(TAKE_PTR(route), link, gw);
if (r < 0)
return r;
}
@@ -527,7 +577,7 @@ static int link_set_routes_to_servers(
return 0;
}
-static int link_set_routes_to_dns(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_routes_to_dns(Link *link, const struct in_addr *gw) {
const struct in_addr *dns;
int r;
@@ -546,10 +596,10 @@ static int link_set_routes_to_dns(Link *link, const struct in_addr *gw) {
if (r < 0)
return r;
- return link_set_routes_to_servers(link, dns, r, gw);
+ return dhcp4_request_routes_to_servers(link, dns, r, gw);
}
-static int link_set_routes_to_ntp(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) {
const struct in_addr *ntp;
int r;
@@ -568,25 +618,17 @@ static int link_set_routes_to_ntp(Link *link, const struct in_addr *gw) {
if (r < 0)
return r;
- return link_set_routes_to_servers(link, ntp, r, gw);
+ return dhcp4_request_routes_to_servers(link, ntp, r, gw);
}
-static int link_set_dhcp_routes(Link *link) {
+static int dhcp4_request_routes(Link *link) {
struct in_addr gw = {};
Route *rt;
int r;
assert(link);
- if (!link->dhcp_lease) /* link went down while we configured the IP addresses? */
- return 0;
-
- if (!link->network) /* link went down while we configured the IP addresses? */
- return 0;
-
- if (!link_has_carrier(link) && !link->network->configure_without_carrier)
- /* During configuring addresses, the link lost its carrier. As networkd is dropping
- * the addresses now, let's not configure the routes either. */
+ if (!link->dhcp_lease)
return 0;
while ((rt = set_steal_first(link->dhcp_routes))) {
@@ -595,28 +637,28 @@ static int link_set_dhcp_routes(Link *link) {
return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
}
- r = link_set_dhcp_prefix_route(link);
+ r = dhcp4_request_prefix_route(link);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not set prefix route: %m");
+ return log_link_error_errno(link, r, "DHCP error: Could not request prefix route: %m");
- r = link_set_dhcp_static_routes(link, &gw);
+ r = dhcp4_request_static_routes(link, &gw);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not set static routes: %m");
+ return log_link_error_errno(link, r, "DHCP error: Could not request static routes: %m");
if (r == 0) {
/* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
* a Router option, the DHCP client MUST ignore the Router option. */
- r = link_set_dhcp_gateway(link, &gw);
+ r = dhcp4_request_gateway(link, &gw);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not set gateway: %m");
+ return log_link_error_errno(link, r, "DHCP error: Could not request gateway: %m");
}
- r = link_set_routes_to_dns(link, &gw);
+ r = dhcp4_request_routes_to_dns(link, &gw);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not set routes to DNS servers: %m");
+ return log_link_error_errno(link, r, "DHCP error: Could not request routes to DNS servers: %m");
- r = link_set_routes_to_ntp(link, &gw);
+ r = dhcp4_request_routes_to_ntp(link, &gw);
if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not set routes to NTP servers: %m");
+ return log_link_error_errno(link, r, "DHCP error: Could not request routes to NTP servers: %m");
return 0;
}
@@ -670,24 +712,20 @@ static int dhcp_reset_hostname(Link *link) {
return 0;
}
-static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int dhcp4_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
- assert(m);
assert(link);
assert(link->dhcp4_remove_messages > 0);
link->dhcp4_remove_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -ESRCH)
- log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 route, ignoring");
+ r = link_route_remove_handler_internal(rtnl, m, link, "Failed to remove DHCPv4 route, ignoring");
+ if (r <= 0)
+ return r;
if (link->dhcp4_remove_messages == 0) {
- r = dhcp4_update_address(link, false);
+ r = dhcp4_request_address_and_routes(link, false);
if (r < 0)
link_enter_failed(link);
}
@@ -695,26 +733,20 @@ static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, L
return 1;
}
-static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int dhcp4_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
- assert(m);
assert(link);
assert(link->dhcp4_remove_messages > 0);
link->dhcp4_remove_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EADDRNOTAVAIL)
- log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring");
- else
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_remove_handler_internal(rtnl, m, link, "Failed to remove DHCPv4 address, ignoring");
+ if (r <= 0)
+ return r;
if (link->dhcp4_remove_messages == 0) {
- r = dhcp4_update_address(link, false);
+ r = dhcp4_request_address_and_routes(link, false);
if (r < 0)
link_enter_failed(link);
}
@@ -729,7 +761,7 @@ static int dhcp4_remove_all(Link *link) {
assert(link);
SET_FOREACH(route, link->dhcp_routes) {
- k = route_remove(route, NULL, link, dhcp4_remove_route_handler);
+ k = route_remove(route, NULL, link, dhcp4_route_remove_handler);
if (k < 0)
r = k;
else
@@ -737,7 +769,7 @@ static int dhcp4_remove_all(Link *link) {
}
if (link->dhcp_address) {
- k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler);
+ k = address_remove(link->dhcp_address, link, dhcp4_address_remove_handler);
if (k < 0)
r = k;
else
@@ -779,12 +811,18 @@ static int dhcp_lease_lost(Link *link) {
(void) sd_ipv4acd_stop(link->dhcp_acd);
- return r;
+ if (r < 0)
+ return r;
+
+ r = link_request_static_nexthops(link, true);
+ if (r < 0)
+ return r;
+
+ return link_request_static_routes(link, true);
}
static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
- _cleanup_free_ char *pretty = NULL;
- union in_addr_union address = {};
+ struct in_addr address;
Link *link;
int r;
@@ -795,23 +833,21 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
switch (event) {
case SD_IPV4ACD_EVENT_STOP:
- log_link_debug(link, "Stopping ACD client for DHCP4...");
+ log_link_debug(link, "Stopping ACD client for DHCPv4 address.");
return;
case SD_IPV4ACD_EVENT_BIND:
if (DEBUG_LOGGING) {
- (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in);
- (void) in_addr_to_string(AF_INET, &address, &pretty);
- log_link_debug(link, "Successfully claimed DHCP4 address %s", strna(pretty));
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ log_link_debug(link, "Successfully claimed DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
}
link->dhcp4_address_bind = true;
dhcp4_check_ready(link);
break;
case SD_IPV4ACD_EVENT_CONFLICT:
- (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in);
- (void) in_addr_to_string(AF_INET, &address, &pretty);
- log_link_warning(link, "DAD conflict. Dropping DHCP4 address %s", strna(pretty));
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ log_link_warning(link, "DAD conflict. Dropping DHCPv4 address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address));
r = sd_dhcp_client_send_decline(link->dhcp_client);
if (r < 0)
@@ -893,8 +929,7 @@ static int dhcp4_dad_update_mac(Link *link) {
}
static int dhcp4_start_acd(Link *link) {
- union in_addr_union addr;
- struct in_addr old;
+ struct in_addr addr, old;
int r;
if (!link->network->dhcp_send_decline)
@@ -907,7 +942,7 @@ static int dhcp4_start_acd(Link *link) {
link->dhcp4_address_bind = false;
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in);
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr);
if (r < 0)
return r;
@@ -915,7 +950,7 @@ static int dhcp4_start_acd(Link *link) {
if (r < 0)
return r;
- r = sd_ipv4acd_set_address(link->dhcp_acd, &addr.in);
+ r = sd_ipv4acd_set_address(link->dhcp_acd, &addr);
if (r < 0)
return r;
@@ -923,45 +958,51 @@ static int dhcp4_start_acd(Link *link) {
if (r < 0)
return r;
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *pretty = NULL;
+ log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(addr));
- (void) in_addr_to_string(AF_INET, &addr, &pretty);
- log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty));
- }
+ return sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr, &old));
+}
- r = sd_ipv4acd_start(link->dhcp_acd, !in4_addr_equal(&addr.in, &old));
- if (r < 0)
- return r;
+static int dhcp4_address_ready_callback(Address *address) {
+ assert(address);
- return 1;
+ /* Do not call this again. */
+ address->callback = NULL;
+
+ dhcp4_check_ready(address->link);
+ return 0;
}
-static int dhcp4_address_ready_callback(Address *address) {
+static int dhcp4_after_address_configure(Request *req, void *object) {
+ Address *address = object;
Link *link;
int r;
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
assert(address);
- link = address->link;
+ link = req->link;
- /* Do not call this again. */
- address->callback = NULL;
-
- r = link_set_dhcp_routes(link);
- if (r < 0)
- return r;
+ if (!address_equal(link->dhcp_address, address)) {
+ if (link->dhcp_address_old &&
+ !address_equal(link->dhcp_address_old, link->dhcp_address)) {
+ /* Still too old address exists? Let's remove it immediately. */
+ r = address_remove(link->dhcp_address_old, link, NULL);
+ if (r < 0)
+ return r;
+ }
+ link->dhcp_address_old = link->dhcp_address;
+ }
- /* Reconfigure static routes as kernel may remove some routes when lease expires. */
- r = link_set_routes(link);
- if (r < 0)
- return r;
+ link->dhcp_address = address;
r = dhcp4_start_acd(link);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCP4 address: %m");
+ return log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCPv4 address: %m");
- dhcp4_check_ready(link);
return 0;
}
@@ -969,36 +1010,30 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
int r;
assert(link);
+ assert(link->dhcp4_messages > 0);
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
+ link->dhcp4_messages--;
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv4 address");
+ if (r <= 0)
+ return r;
if (address_is_ready(link->dhcp_address)) {
r = dhcp4_address_ready_callback(link->dhcp_address);
- if (r < 0) {
+ if (r < 0)
link_enter_failed(link);
- return 1;
- }
} else
link->dhcp_address->callback = dhcp4_address_ready_callback;
return 1;
}
-static int dhcp4_update_address(Link *link, bool announce) {
+static int dhcp4_request_address(Link *link, bool announce) {
_cleanup_(address_freep) Address *addr = NULL;
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
struct in_addr address, netmask;
unsigned prefixlen;
- Address *ret;
+ Request *req;
int r;
assert(link);
@@ -1007,15 +1042,6 @@ static int dhcp4_update_address(Link *link, bool announce) {
if (!link->dhcp_lease)
return 0;
- link_set_state(link, LINK_STATE_CONFIGURING);
- link->dhcp4_configured = false;
-
- /* address_handler calls link_set_routes() and link_set_nexthop(). Before they are called, the
- * related flags must be cleared. Otherwise, the link becomes configured state before routes
- * are configured. */
- link->static_routes_configured = false;
- link->static_nexthops_configured = false;
-
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no address: %m");
@@ -1073,15 +1099,34 @@ static int dhcp4_update_address(Link *link, bool announce) {
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
addr->route_metric = link->network->dhcp_route_metric;
- /* allow reusing an existing address and simply update its lifetime
- * in case it already exists */
- r = address_configure(addr, link, dhcp4_address_handler, &ret);
+ if (address_get(link, addr, NULL) < 0)
+ link->dhcp4_configured = false;
+
+ r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages,
+ dhcp4_address_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m");
+ return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m");
- if (!address_equal(link->dhcp_address, ret))
- link->dhcp_address_old = link->dhcp_address;
- link->dhcp_address = ret;
+ req->after_configure = dhcp4_after_address_configure;
+
+ return 0;
+}
+
+static int dhcp4_request_address_and_routes(Link *link, bool announce) {
+ int r;
+
+ assert(link);
+
+ r = dhcp4_request_address(link, announce);
+ if (r < 0)
+ return r;
+
+ r = dhcp4_request_routes(link);
+ if (r < 0)
+ return r;
+
+ link_set_state(link, LINK_STATE_CONFIGURING);
+ link_check_ready(link);
return 0;
}
@@ -1101,7 +1146,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
link->dhcp_lease = sd_dhcp_lease_ref(lease);
link_dirty(link);
- return dhcp4_update_address(link, false);
+ return dhcp4_request_address_and_routes(link, false);
}
static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
@@ -1166,16 +1211,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
- if (link->dhcp4_remove_messages == 0) {
- r = dhcp4_update_address(link, true);
- if (r < 0)
- return r;
- } else
- log_link_debug(link,
- "The link has previously assigned DHCPv4 address or routes. "
- "The newly assigned address and routes will set up after old ones are removed.");
-
- return 0;
+ return dhcp4_request_address_and_routes(link, true);
}
static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index b13a635a78..7061afe0a2 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -18,6 +18,7 @@
#include "networkd-dhcp6.h"
#include "networkd-link.h"
#include "networkd-manager.h"
+#include "networkd-queue.h"
#include "networkd-radv.h"
#include "siphash24.h"
#include "string-table.h"
@@ -43,7 +44,7 @@ bool link_dhcp6_pd_is_enabled(Link *link) {
static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
uint32_t lifetime_preferred, lifetime_valid;
- union in_addr_union pd_prefix;
+ struct in6_addr pd_prefix;
uint8_t pd_prefix_len;
if (!lease)
@@ -51,7 +52,7 @@ static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
sd_dhcp6_lease_reset_pd_prefix_iter(lease);
- return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
+ return sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
}
DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) {
@@ -86,21 +87,21 @@ static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6Deleg
DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func);
-static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) {
+static Link *dhcp6_pd_get_link_by_prefix(Link *link, const struct in6_addr *prefix) {
DHCP6DelegatedPrefix *pd;
assert(link);
assert(link->manager);
assert(prefix);
- pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6);
+ pd = hashmap_get(link->manager->dhcp6_prefixes, prefix);
if (!pd)
return NULL;
return pd->link;
}
-static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) {
+static int dhcp6_pd_get_assigned_prefix(Link *link, const struct in6_addr *pd_prefix, struct in6_addr *ret_prefix) {
DHCP6DelegatedPrefix *pd, in;
assert(link);
@@ -109,7 +110,7 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *p
assert(ret_prefix);
in = (DHCP6DelegatedPrefix) {
- .pd_prefix = pd_prefix->in6,
+ .pd_prefix = *pd_prefix,
.link = link,
};
@@ -117,7 +118,7 @@ static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *p
if (!pd)
return -ENOENT;
- ret_prefix->in6 = pd->prefix;
+ *ret_prefix = pd->prefix;
return 0;
}
@@ -229,7 +230,7 @@ int dhcp6_pd_remove(Link *link) {
return r;
}
-static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+static int dhcp6_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -237,15 +238,9 @@ static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *l
link->dhcp6_pd_route_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route");
- link_enter_failed(link);
- return 1;
- }
+ r = route_configure_handler_internal(rtnl, m, link, "Failed to add DHCPv6 Prefix Delegation route");
+ if (r <= 0)
+ return r;
if (link->dhcp6_pd_route_messages == 0) {
log_link_debug(link, "DHCPv6 prefix delegation routes set");
@@ -264,11 +259,32 @@ static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *l
return 1;
}
-static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) {
+static int dhcp6_pd_after_route_configure(Request *req, void *object) {
+ Route *route = object;
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ROUTE);
+ assert(route);
+
+ link = req->link;
+
+ r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m");
+
+ set_remove(link->dhcp6_pd_routes_old, route);
+
+ return 0;
+}
+
+static int dhcp6_pd_request_route(Link *link, const struct in6_addr *prefix, const struct in6_addr *pd_prefix) {
_cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL;
_cleanup_(route_freep) Route *route = NULL;
Link *assigned_link;
- Route *ret;
+ Request *req;
int r;
assert(link);
@@ -281,24 +297,23 @@ static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, con
return r;
route->family = AF_INET6;
- route->dst = *prefix;
+ route->dst.in6 = *prefix;
route->dst_prefixlen = 64;
route->protocol = RTPROT_DHCP;
route->priority = link->network->dhcp6_pd_route_metric;
- r = route_configure(route, link, dhcp6_pd_route_handler, &ret);
+ r = link_has_route(link, route);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m");
- if (r > 0)
+ return r;
+ if (r == 0)
link->dhcp6_pd_route_configured = false;
- link->dhcp6_pd_route_messages++;
-
- r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret);
+ r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_pd_route_messages,
+ dhcp6_pd_route_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m");
+ return log_link_error_errno(link, r, "Failed to request DHCPv6 prefix route: %m");
- (void) set_remove(link->dhcp6_pd_routes_old, ret);
+ req->after_configure = dhcp6_pd_after_route_configure;
assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix);
if (assigned_link) {
@@ -311,8 +326,8 @@ static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, con
return log_oom();
*pd = (DHCP6DelegatedPrefix) {
- .prefix = prefix->in6,
- .pd_prefix = pd_prefix->in6,
+ .prefix = *prefix,
+ .pd_prefix = *pd_prefix,
.link = link_ref(link),
};
@@ -338,16 +353,9 @@ static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin
link->dhcp6_pd_address_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 delegated prefix address");
+ if (r <= 0)
+ return r;
if (link->dhcp6_pd_address_messages == 0) {
log_link_debug(link, "DHCPv6 delegated prefix addresses set");
@@ -370,12 +378,15 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) {
_cleanup_free_ char *buffer = NULL;
int log_level;
+ assert(address);
+ assert(address->family == AF_INET6);
+
log_level = address_get(link, address, NULL) >= 0 ? LOG_DEBUG : LOG_INFO;
if (log_level < log_get_max_level())
return;
- (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buffer);
+ (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer);
if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
address->cinfo.ifa_valid * USEC_PER_SEC,
@@ -391,14 +402,35 @@ static void log_dhcp6_pd_address(Link *link, const Address *address) {
preferred_str ? "for " : "forever", strempty(preferred_str));
}
-static int dhcp6_set_pd_address(
+static int dhcp6_pd_after_address_configure(Request *req, void *object) {
+ Address *address = object;
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
+ assert(address);
+
+ link = req->link;
+
+ r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, address);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m");
+
+ set_remove(link->dhcp6_pd_addresses_old, address);
+
+ return 0;
+}
+
+static int dhcp6_pd_request_address(
Link *link,
- const union in_addr_union *prefix,
+ const struct in6_addr *prefix,
uint32_t lifetime_preferred,
uint32_t lifetime_valid) {
_cleanup_(address_freep) Address *address = NULL;
- Address *ret;
+ Request *req;
int r;
assert(link);
@@ -412,10 +444,10 @@ static int dhcp6_set_pd_address(
if (r < 0)
return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m");
- address->in_addr = *prefix;
+ address->in_addr.in6 = *prefix;
- if (in_addr_is_set(AF_INET6, &link->network->dhcp6_pd_token))
- memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8);
+ if (in6_addr_is_set(&link->network->dhcp6_pd_token))
+ memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.s6_addr + 8, 8);
else {
r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
if (r < 0)
@@ -430,27 +462,24 @@ static int dhcp6_set_pd_address(
address->route_metric = link->network->dhcp6_pd_route_metric;
log_dhcp6_pd_address(link, address);
- r = address_configure(address, link, dhcp6_pd_address_handler, &ret);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m");
- if (r > 0)
- link->dhcp6_pd_address_configured = false;
- link->dhcp6_pd_address_messages++;
+ if (address_get(link, address, NULL) < 0)
+ link->dhcp6_pd_address_configured = false;
- r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret);
+ r = link_request_address(link, TAKE_PTR(address), true, &link->dhcp6_pd_address_messages,
+ dhcp6_pd_address_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m");
+ return log_link_error_errno(link, r, "Failed to request DHCPv6 delegated prefix address: %m");
- (void) set_remove(link->dhcp6_pd_addresses_old, ret);
+ req->after_configure = dhcp6_pd_after_address_configure;
return 0;
}
static int dhcp6_pd_assign_prefix(
Link *link,
- const union in_addr_union *prefix,
- const union in_addr_union *pd_prefix,
+ const struct in6_addr *prefix,
+ const struct in6_addr *pd_prefix,
uint32_t lifetime_preferred,
uint32_t lifetime_valid) {
@@ -461,16 +490,16 @@ static int dhcp6_pd_assign_prefix(
assert(prefix);
if (link->network->dhcp6_pd_announce) {
- r = radv_add_prefix(link, &prefix->in6, 64, lifetime_preferred, lifetime_valid);
+ r = radv_add_prefix(link, prefix, 64, lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
}
- r = dhcp6_set_pd_route(link, prefix, pd_prefix);
+ r = dhcp6_pd_request_route(link, prefix, pd_prefix);
if (r < 0)
return r;
- r = dhcp6_set_pd_address(link, prefix, lifetime_preferred, lifetime_valid);
+ r = dhcp6_pd_request_address(link, prefix, lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
@@ -486,9 +515,9 @@ static bool link_has_preferred_subnet_id(Link *link) {
static int dhcp6_get_preferred_delegated_prefix(
Link *link,
- const union in_addr_union *masked_pd_prefix,
+ const struct in6_addr *masked_pd_prefix,
uint8_t pd_prefix_len,
- union in_addr_union *ret) {
+ struct in6_addr *ret) {
/* We start off with the original PD prefix we have been assigned and iterate from there */
union in_addr_union prefix;
@@ -502,7 +531,7 @@ static int dhcp6_get_preferred_delegated_prefix(
assert(pd_prefix_len <= 64);
n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
- prefix = *masked_pd_prefix;
+ prefix.in6 = *masked_pd_prefix;
if (link_has_preferred_subnet_id(link)) {
uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
@@ -521,28 +550,28 @@ static int dhcp6_get_preferred_delegated_prefix(
/* Verify that the prefix we did calculate fits in the pd prefix.
* This should not fail as we checked the prefix size beforehand */
- assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0);
+ assert_se(in_addr_prefix_covers(AF_INET6, (const union in_addr_union*) masked_pd_prefix, pd_prefix_len, &prefix) > 0);
- assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
+ assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6);
if (assigned_link && assigned_link != link) {
_cleanup_free_ char *assigned_buf = NULL;
- (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
+ (void) in6_addr_to_string(&prefix.in6, &assigned_buf);
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
"The requested prefix %s is already assigned to another link.",
strna(assigned_buf));
}
- *ret = prefix;
+ *ret = prefix.in6;
return 0;
}
for (uint64_t n = 0; n < n_prefixes; n++) {
/* If we do not have an allocation preference just iterate
* through the address space and return the first free prefix. */
- assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
+ assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix.in6);
if (!assigned_link || assigned_link == link) {
- *ret = prefix;
+ *ret = prefix.in6;
return 0;
}
@@ -555,7 +584,7 @@ static int dhcp6_get_preferred_delegated_prefix(
}
static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
- const union in_addr_union *masked_pd_prefix,
+ const struct in6_addr *masked_pd_prefix,
uint8_t pd_prefix_len,
uint32_t lifetime_preferred,
uint32_t lifetime_valid,
@@ -571,7 +600,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
HASHMAP_FOREACH(link, dhcp6_link->manager->links) {
_cleanup_free_ char *assigned_buf = NULL;
- union in_addr_union assigned_prefix;
+ struct in6_addr assigned_prefix;
if (link == dhcp6_link)
continue;
@@ -594,7 +623,7 @@ static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
}
}
- (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf);
+ (void) in6_addr_to_string(&assigned_prefix, &assigned_buf);
r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix,
lifetime_preferred, lifetime_valid);
if (r < 0) {
@@ -659,11 +688,10 @@ static int dhcp6_pd_finalize(Link *link) {
if (r < 0)
return r;
- if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured)
- link_check_ready(link);
- else
+ if (!link->dhcp6_pd_address_configured || !link->dhcp6_pd_route_configured)
link_set_state(link, LINK_STATE_CONFIGURING);
+ link_check_ready(link);
return 0;
}
@@ -781,7 +809,7 @@ static int dhcp6_remove(Link *link) {
return r;
}
-static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+static int dhcp6_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
@@ -789,15 +817,9 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link
link->dhcp6_route_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet");
- link_enter_failed(link);
- return 1;
- }
+ r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated subnet");
+ if (r <= 0)
+ return r;
if (link->dhcp6_route_messages == 0) {
log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set");
@@ -815,16 +837,37 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link
return 1;
}
-static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) {
+static int dhcp6_after_route_configure(Request *req, void *object) {
+ Route *route = object;
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ROUTE);
+ assert(route);
+
+ link = req->link;
+
+ r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet: %m");
+
+ set_remove(link->dhcp6_routes_old, route);
+
+ return 0;
+}
+
+static int dhcp6_request_unreachable_route(Link *link, const struct in6_addr *addr, uint8_t prefixlen) {
_cleanup_(route_freep) Route *route = NULL;
_cleanup_free_ char *buf = NULL;
- Route *ret;
+ Request *req;
int r;
assert(link);
assert(addr);
- (void) in_addr_prefix_to_string(AF_INET6, addr, prefixlen, &buf);
+ (void) in6_addr_prefix_to_string(addr, prefixlen, &buf);
if (prefixlen == 64) {
log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s since distributed prefix is 64",
@@ -837,32 +880,30 @@ static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *ad
return log_oom();
route->family = AF_INET6;
- route->dst = *addr;
+ route->dst.in6 = *addr;
route->dst_prefixlen = prefixlen;
route->table = link_get_dhcp_route_table(link);
route->type = RTN_UNREACHABLE;
route->protocol = RTPROT_DHCP;
- r = route_configure(route, link, dhcp6_route_handler, &ret);
+ r = link_has_route(link, route);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s: %m",
- strna(buf));
- if (r > 0)
+ return r;
+ if (r == 0)
link->dhcp6_route_configured = false;
- link->dhcp6_route_messages++;
-
- r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret);
+ r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp6_route_messages,
+ dhcp6_route_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s: %m",
+ return log_link_error_errno(link, r, "Failed to request unreachable route for DHCPv6 delegated subnet %s: %m",
strna(buf));
- (void) set_remove(link->dhcp6_routes_old, ret);
+ req->after_configure = dhcp6_after_route_configure;
return 0;
}
-static int dhcp6_pd_prefix_add(Link *link, const union in_addr_union *prefix, uint8_t prefixlen) {
+static int dhcp6_pd_prefix_add(Link *link, const struct in6_addr *prefix, uint8_t prefixlen) {
_cleanup_free_ struct in_addr_prefix *p = NULL;
_cleanup_free_ char *buf = NULL;
int r;
@@ -877,10 +918,10 @@ static int dhcp6_pd_prefix_add(Link *link, const union in_addr_union *prefix, ui
*p = (struct in_addr_prefix) {
.family = AF_INET6,
.prefixlen = prefixlen,
- .address = *prefix,
+ .address.in6 = *prefix,
};
- (void) in_addr_prefix_to_string(p->family, &p->address, p->prefixlen, &buf);
+ (void) in6_addr_prefix_to_string(prefix, prefixlen, &buf);
log_link_full(link,
set_contains(link->dhcp6_pd_prefixes, p) ? LOG_DEBUG :
@@ -918,10 +959,11 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) {
uint32_t lifetime_preferred, lifetime_valid;
- union in_addr_union pd_prefix, prefix;
+ struct in6_addr pd_prefix;
+ union in_addr_union prefix;
uint8_t pd_prefix_len;
- r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len,
+ r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix, &pd_prefix_len,
&lifetime_preferred, &lifetime_valid);
if (r < 0)
break;
@@ -932,7 +974,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
if (r == 0)
continue;
- r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
+ r = dhcp6_request_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
if (r < 0)
return r;
@@ -947,7 +989,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
assert(pd_prefix_len <= 64);
- prefix = pd_prefix;
+ prefix.in6 = pd_prefix;
r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
if (r < 0)
return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m");
@@ -956,20 +998,20 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
_cleanup_free_ char *buf = NULL;
- (void) in_addr_prefix_to_string(AF_INET6, &prefix, pd_prefix_len, &buf);
+ (void) in6_addr_prefix_to_string(&prefix.in6, pd_prefix_len, &buf);
log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s",
n_prefixes, strna(buf));
}
dhcp6_pd_prefix_distribute(dhcp6_link,
- &prefix,
+ &prefix.in6,
pd_prefix_len,
lifetime_preferred,
lifetime_valid,
true);
dhcp6_pd_prefix_distribute(dhcp6_link,
- &prefix,
+ &prefix.in6,
pd_prefix_len,
lifetime_preferred,
lifetime_valid,
@@ -996,16 +1038,9 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
link->dhcp6_address_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address");
+ if (r <= 0)
+ return r;
if (link->dhcp6_address_messages == 0) {
log_link_debug(link, "DHCPv6 addresses set");
@@ -1032,8 +1067,9 @@ static void log_dhcp6_address(Link *link, const Address *address, char **ret) {
assert(link);
assert(address);
+ assert(address->family == AF_INET6);
- (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buffer);
+ (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buffer);
if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
address->cinfo.ifa_valid * USEC_PER_SEC,
@@ -1085,7 +1121,28 @@ finalize:
*ret = TAKE_PTR(buffer);
}
-static int dhcp6_update_address(
+static int dhcp6_after_address_configure(Request *req, void *object) {
+ Address *address = object;
+ Link *link;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
+ assert(address);
+
+ link = req->link;
+
+ r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, address);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to store DHCPv6 address: %m");
+
+ set_remove(link->dhcp6_addresses_old, address);
+
+ return 0;
+}
+
+static int dhcp6_request_address(
Link *link,
const struct in6_addr *ip6_addr,
uint32_t lifetime_preferred,
@@ -1093,7 +1150,7 @@ static int dhcp6_update_address(
_cleanup_(address_freep) Address *addr = NULL;
_cleanup_free_ char *buffer = NULL;
- Address *ret;
+ Request *req;
int r;
r = address_new(&addr);
@@ -1109,19 +1166,15 @@ static int dhcp6_update_address(
log_dhcp6_address(link, addr, &buffer);
- r = address_configure(addr, link, dhcp6_address_handler, &ret);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s: %m", strna(buffer));
- if (r > 0)
+ if (address_get(link, addr, NULL) < 0)
link->dhcp6_address_configured = false;
- link->dhcp6_address_messages++;
-
- r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret);
+ r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp6_address_messages,
+ dhcp6_address_handler, &req);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s: %m", strna(buffer));
+ return log_link_error_errno(link, r, "Failed to request DHCPv6 address %s: %m", strna(buffer));
- (void) set_remove(link->dhcp6_addresses_old, ret);
+ req->after_configure = dhcp6_after_address_configure;
return 0;
}
@@ -1144,7 +1197,7 @@ static int dhcp6_address_acquired(Link *link) {
if (r < 0)
break;
- r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid);
+ r = dhcp6_request_address(link, &ip6_addr, lifetime_preferred, lifetime_valid);
if (r < 0)
return r;
}
@@ -1224,11 +1277,10 @@ static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
if (r < 0)
return r;
- if (link->dhcp6_address_configured && link->dhcp6_route_configured)
- link_check_ready(link);
- else
+ if (!link->dhcp6_address_configured || !link->dhcp6_route_configured)
link_set_state(link, LINK_STATE_CONFIGURING);
+ link_check_ready(link);
return 0;
}
@@ -1298,7 +1350,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
}
}
-int dhcp6_request_address(Link *link, int ir) {
+int dhcp6_request_information(Link *link, int ir) {
int r, inf_req, pd;
bool running;
@@ -1376,7 +1428,7 @@ int dhcp6_start(Link *link) {
log_link_debug(link, "Acquiring DHCPv6 lease");
- return dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+ return dhcp6_request_information(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
}
int dhcp6_request_prefix_delegation(Link *link) {
@@ -1849,7 +1901,8 @@ int config_parse_dhcp6_pd_token(
void *data,
void *userdata) {
- union in_addr_union *addr = data, tmp;
+ struct in6_addr *addr = data;
+ union in_addr_union tmp;
int r;
assert(filename);
@@ -1858,7 +1911,7 @@ int config_parse_dhcp6_pd_token(
assert(data);
if (isempty(rvalue)) {
- *addr = IN_ADDR_NULL;
+ *addr = (struct in6_addr) {};
return 0;
}
@@ -1875,7 +1928,7 @@ int config_parse_dhcp6_pd_token(
return 0;
}
- *addr = tmp;
+ *addr = tmp.in6;
return 0;
}
diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h
index 025bbb6188..f24d6eea5a 100644
--- a/src/network/networkd-dhcp6.h
+++ b/src/network/networkd-dhcp6.h
@@ -32,7 +32,7 @@ int dhcp6_pd_remove(Link *link);
int dhcp6_configure(Link *link);
int dhcp6_update_mac(Link *link);
int dhcp6_start(Link *link);
-int dhcp6_request_address(Link *link, int ir);
+int dhcp6_request_information(Link *link, int ir);
int dhcp6_request_prefix_delegation(Link *link);
int link_serialize_dhcp6_client(Link *link, FILE *f);
diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c
index 5390c53799..8e2d761cf9 100644
--- a/src/network/networkd-ipv4ll.c
+++ b/src/network/networkd-ipv4ll.c
@@ -8,39 +8,55 @@
#include "networkd-ipv4ll.h"
#include "networkd-link.h"
#include "networkd-manager.h"
+#include "networkd-queue.h"
#include "parse-util.h"
-static int ipv4ll_address_lost(Link *link) {
+static int address_new_from_ipv4ll(Link *link, Address **ret) {
_cleanup_(address_freep) Address *address = NULL;
struct in_addr addr;
int r;
assert(link);
-
- link->ipv4ll_address_configured = false;
+ assert(link->ipv4ll);
+ assert(ret);
r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
if (r < 0)
- return 0;
-
- log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(addr));
+ return r;
r = address_new(&address);
if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate address: %m");
+ return -ENOMEM;
address->family = AF_INET;
address->in_addr.in = addr;
address->prefixlen = 16;
+ address->broadcast.s_addr = address->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> address->prefixlen);
address->scope = RT_SCOPE_LINK;
+ address->route_metric = IPV4LL_ROUTE_METRIC;
+
+ *ret = TAKE_PTR(address);
+ return 0;
+}
+
+static int ipv4ll_address_lost(Link *link) {
+ _cleanup_(address_freep) Address *address = NULL;
+ int r;
+
+ assert(link);
+
+ link->ipv4ll_address_configured = false;
- r = address_remove(address, link, NULL);
+ r = address_new_from_ipv4ll(link, &address);
+ if (r == -ENOENT)
+ return 0;
if (r < 0)
return r;
- link_check_ready(link);
+ log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
- return 0;
+ return address_remove(address, link, NULL);
}
static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -49,13 +65,9 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
assert(link);
assert(!link->ipv4ll_address_configured);
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "could not set ipv4ll address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_configure_handler_internal(rtnl, m, link, "Could not set ipv4ll address");
+ if (r <= 0)
+ return r;
link->ipv4ll_address_configured = true;
link_check_ready(link);
@@ -64,8 +76,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
}
static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
- _cleanup_(address_freep) Address *ll_addr = NULL;
- struct in_addr address;
+ _cleanup_(address_freep) Address *address = NULL;
int r;
assert(ll);
@@ -73,31 +84,16 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
link->ipv4ll_address_configured = false;
- r = sd_ipv4ll_get_address(ll, &address);
+ r = address_new_from_ipv4ll(link, &address);
if (r == -ENOENT)
return 0;
- else if (r < 0)
- return r;
-
- log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR,
- IPV4_ADDRESS_FMT_VAL(address));
-
- r = address_new(&ll_addr);
if (r < 0)
return r;
- ll_addr->family = AF_INET;
- ll_addr->in_addr.in = address;
- ll_addr->prefixlen = 16;
- ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
- ll_addr->scope = RT_SCOPE_LINK;
- ll_addr->route_metric = IPV4LL_ROUTE_METRIC;
-
- r = address_configure(ll_addr, link, ipv4ll_address_handler, NULL);
- if (r < 0)
- return r;
+ log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR,
+ IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
- return 0;
+ return link_request_address(link, TAKE_PTR(address), true, NULL, ipv4ll_address_handler, NULL);
}
static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index ffaebbbf6d..152ac05e7c 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -33,6 +33,7 @@
#include "networkd-dhcp6.h"
#include "networkd-fdb.h"
#include "networkd-ipv4ll.h"
+#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
#include "networkd-lldp-tx.h"
@@ -41,11 +42,12 @@
#include "networkd-ndisc.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
-#include "networkd-sriov.h"
-#include "networkd-sysctl.h"
+#include "networkd-queue.h"
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
+#include "networkd-sriov.h"
#include "networkd-state-file.h"
+#include "networkd-sysctl.h"
#include "networkd-wifi.h"
#include "set.h"
#include "socket-util.h"
@@ -133,6 +135,25 @@ bool link_ipv6_enabled(Link *link) {
return false;
}
+bool link_is_ready_to_configure(Link *link, bool allow_unmanaged) {
+ assert(link);
+
+ if (!link->network || link->network->unmanaged) {
+ if (!allow_unmanaged)
+ return false;
+
+ return link_has_carrier(link);
+ }
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return false;
+
+ if (!link_has_carrier(link) && !link->network->configure_without_carrier)
+ return false;
+
+ return true;
+}
+
static bool link_is_enslaved(Link *link) {
if (link->flags & IFF_SLAVE)
/* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */
@@ -773,12 +794,9 @@ void link_check_ready(Link *link) {
return;
}
- if (!link->addresses_configured)
+ if (!link->static_addresses_configured)
return (void) log_link_debug(link, "%s(): static addresses are not configured.", __func__);
- if (!link->neighbors_configured)
- return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
-
SET_FOREACH(a, link->addresses)
if (!address_is_ready(a)) {
_cleanup_free_ char *str = NULL;
@@ -787,13 +805,16 @@ void link_check_ready(Link *link) {
return (void) log_link_debug(link, "%s(): an address %s is not ready.", __func__, strna(str));
}
- if (!link->static_routes_configured)
- return (void) log_link_debug(link, "%s(): static routes are not configured.", __func__);
+ if (!link->static_neighbors_configured)
+ return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
if (!link->static_nexthops_configured)
return (void) log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
- if (!link->routing_policy_rules_configured)
+ if (!link->static_routes_configured)
+ return (void) log_link_debug(link, "%s(): static routes are not configured.", __func__);
+
+ if (!link->static_routing_policy_rules_configured)
return (void) log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__);
if (!link->tc_configured)
@@ -857,15 +878,6 @@ static int link_set_static_configs(Link *link) {
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
- /* Reset all *_configured flags we are configuring. */
- link->request_static_addresses = false;
- link->addresses_configured = false;
- link->addresses_ready = false;
- link->neighbors_configured = false;
- link->static_routes_configured = false;
- link->static_nexthops_configured = false;
- link->routing_policy_rules_configured = false;
-
r = link_set_bridge_fdb(link);
if (r < 0)
return r;
@@ -874,20 +886,31 @@ static int link_set_static_configs(Link *link) {
if (r < 0)
return r;
- r = link_set_neighbors(link);
+ r = link_set_ipv6_proxy_ndp_addresses(link);
+ if (r < 0)
+ return r;
+
+ r = link_set_address_labels(link);
if (r < 0)
return r;
- r = link_set_addresses(link);
+ r = link_request_static_addresses(link);
if (r < 0)
return r;
- r = link_set_address_labels(link);
+ r = link_request_static_neighbors(link);
+ if (r < 0)
+ return r;
+
+ r = link_request_static_nexthops(link, false);
if (r < 0)
return r;
- /* now that we can figure out a default address for the dhcp server, start it */
- r = dhcp4_server_configure(link);
+ r = link_request_static_routes(link, false);
+ if (r < 0)
+ return r;
+
+ r = link_request_static_routing_policy_rules(link);
if (r < 0)
return r;
@@ -2028,17 +2051,17 @@ static int link_drop_foreign_config(Link *link) {
assert(link);
assert(link->manager);
- r = link_drop_foreign_addresses(link);
+ r = link_drop_foreign_routes(link);
- k = link_drop_foreign_neighbors(link);
+ k = link_drop_foreign_nexthops(link);
if (k < 0 && r >= 0)
r = k;
- k = link_drop_foreign_routes(link);
+ k = link_drop_foreign_addresses(link);
if (k < 0 && r >= 0)
r = k;
- k = link_drop_foreign_nexthops(link);
+ k = link_drop_foreign_neighbors(link);
if (k < 0 && r >= 0)
r = k;
@@ -2055,17 +2078,17 @@ static int link_drop_config(Link *link) {
assert(link);
assert(link->manager);
- r = link_drop_addresses(link);
+ r = link_drop_routes(link);
- k = link_drop_neighbors(link);
+ k = link_drop_nexthops(link);
if (k < 0 && r >= 0)
r = k;
- k = link_drop_routes(link);
+ k = link_drop_addresses(link);
if (k < 0 && r >= 0)
r = k;
- k = link_drop_nexthops(link);
+ k = link_drop_neighbors(link);
if (k < 0 && r >= 0)
r = k;
@@ -2078,6 +2101,17 @@ static int link_drop_config(Link *link) {
return r;
}
+static void link_drop_requests(Link *link) {
+ Request *req;
+
+ assert(link);
+ assert(link->manager);
+
+ ORDERED_SET_FOREACH(req, link->manager->request_queue)
+ if (req->link == link)
+ request_drop(req);
+}
+
int link_configure(Link *link) {
int r;
@@ -2232,6 +2266,8 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
if (r < 0)
return r;
+ link_drop_requests(link);
+
r = link_drop_config(link);
if (r < 0)
return r;
@@ -2664,6 +2700,8 @@ static int link_carrier_lost(Link *link) {
return r;
}
+ link_drop_requests(link);
+
r = link_drop_config(link);
if (r < 0)
return r;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index b560dde0dc..fbc593f124 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -78,13 +78,16 @@ typedef struct Link {
LinkAddressState ipv4_address_state;
LinkAddressState ipv6_address_state;
- unsigned address_messages;
- unsigned address_remove_messages;
unsigned address_label_messages;
- unsigned neighbor_messages;
- unsigned route_messages;
- unsigned nexthop_messages;
- unsigned routing_policy_rule_messages;
+ unsigned static_address_messages;
+ unsigned static_neighbor_messages;
+ unsigned static_nexthop_messages;
+ unsigned static_route_messages;
+ unsigned static_routing_policy_rule_messages;
+ unsigned address_remove_messages;
+ unsigned neighbor_remove_messages;
+ unsigned nexthop_remove_messages;
+ unsigned route_remove_messages;
unsigned tc_messages;
unsigned sr_iov_messages;
unsigned enslaving;
@@ -118,13 +121,11 @@ typedef struct Link {
sd_ipv4ll *ipv4ll;
bool ipv4ll_address_configured:1;
- bool request_static_addresses:1;
- bool addresses_configured:1;
- bool addresses_ready:1;
- bool neighbors_configured:1;
- bool static_routes_configured:1;
+ bool static_addresses_configured:1;
+ bool static_neighbors_configured:1;
bool static_nexthops_configured:1;
- bool routing_policy_rules_configured:1;
+ bool static_routes_configured:1;
+ bool static_routing_policy_rules_configured:1;
bool tc_configured:1;
bool sr_iov_configured:1;
bool setting_mtu:1;
@@ -199,6 +200,8 @@ typedef struct Link {
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
+bool link_is_ready_to_configure(Link *link, bool allow_unmanaged);
+
void link_ntp_settings_clear(Link *link);
void link_dns_settings_clear(Link *link);
Link *link_unref(Link *link);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 562ce5ca54..fd576169a9 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -34,6 +34,7 @@
#include "networkd-neighbor.h"
#include "networkd-network-bus.h"
#include "networkd-nexthop.h"
+#include "networkd-queue.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-speed-meter.h"
#include "networkd-state-file.h"
@@ -406,6 +407,10 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
+ r = sd_event_add_post(m->event, NULL, manager_process_requests, m);
+ if (r < 0)
+ return r;
+
r = manager_connect_rtnl(m);
if (r < 0)
return r;
@@ -446,6 +451,8 @@ Manager* manager_free(Manager *m) {
HASHMAP_FOREACH(link, m->links)
(void) link_stop_engines(link, true);
+ m->request_queue = ordered_set_free_with_destructor(m->request_queue, request_free);
+
m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 301b97c1a1..3f8f81b865 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -62,6 +62,7 @@ struct Manager {
char* dynamic_hostname;
char* dynamic_timezone;
+ unsigned routing_policy_rule_remove_messages;
Set *rules;
Set *rules_foreign;
@@ -69,10 +70,12 @@ struct Manager {
Hashmap *nexthops_by_id;
/* Manager stores nexthops without RTA_OIF attribute. */
+ unsigned nexthop_remove_messages;
Set *nexthops;
Set *nexthops_foreign;
/* Manager stores routes without RTA_OIF attribute. */
+ unsigned route_remove_messages;
Set *routes;
Set *routes_foreign;
@@ -91,6 +94,8 @@ struct Manager {
bool bridge_mdb_on_master_not_supported;
FirewallContext *fw_ctx;
+
+ OrderedSet *request_queue;
};
int manager_new(Manager **ret);
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index 988c673255..cfcf4cc36a 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -15,6 +15,7 @@
#include "networkd-dhcp6.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
+#include "networkd-queue.h"
#include "networkd-state-file.h"
#include "string-table.h"
#include "string-util.h"
@@ -89,6 +90,7 @@ static int ndisc_address_callback(Address *address) {
assert(address);
assert(address->link);
+ assert(address->family == AF_INET6);
SET_FOREACH(n, address->link->ndisc_addresses)
if (n->address == address) {
@@ -99,7 +101,7 @@ static int ndisc_address_callback(Address *address) {
if (in6_addr_is_null(&router)) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_prefix_to_string(address->family, &address->in_addr, address->prefixlen, &buf);
+ (void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buf);
log_link_debug(address->link, "%s is called for %s, but it is already removed, ignoring.",
__func__, strna(buf));
return 0;
@@ -107,7 +109,7 @@ static int ndisc_address_callback(Address *address) {
/* Make this called only once */
SET_FOREACH(n, address->link->ndisc_addresses)
- if (IN6_ARE_ADDR_EQUAL(&n->router, &router))
+ if (in6_addr_equal(&n->router, &router))
n->address->callback = NULL;
return ndisc_remove_old_one(address->link, &router, true);
@@ -131,7 +133,7 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
return 0;
SET_FOREACH(na, link->ndisc_addresses)
- if (!na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router)) {
+ if (!na->marked && in6_addr_equal(&na->router, router)) {
set_callback = true;
break;
}
@@ -145,13 +147,13 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
if (set_callback) {
SET_FOREACH(na, link->ndisc_addresses)
- if (!na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router))
+ if (!na->marked && in6_addr_equal(&na->router, router))
na->address->callback = ndisc_address_callback;
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) router, &buf);
+ (void) in6_addr_to_string(router, &buf);
log_link_debug(link, "No SLAAC address obtained from %s is ready. "
"The old NDisc information will be removed later.",
strna(buf));
@@ -163,32 +165,32 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) router, &buf);
+ (void) in6_addr_to_string(router, &buf);
log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf));
}
SET_FOREACH(na, link->ndisc_addresses)
- if (na->marked && IN6_ARE_ADDR_EQUAL(&na->router, router)) {
+ if (na->marked && in6_addr_equal(&na->router, router)) {
k = address_remove(na->address, link, NULL);
if (k < 0)
r = k;
}
SET_FOREACH(nr, link->ndisc_routes)
- if (nr->marked && IN6_ARE_ADDR_EQUAL(&nr->router, router)) {
+ if (nr->marked && in6_addr_equal(&nr->router, router)) {
k = route_remove(nr->route, NULL, link, NULL);
if (k < 0)
r = k;
}
SET_FOREACH(rdnss, link->ndisc_rdnss)
- if (rdnss->marked && IN6_ARE_ADDR_EQUAL(&rdnss->router, router)) {
+ if (rdnss->marked && in6_addr_equal(&rdnss->router, router)) {
free(set_remove(link->ndisc_rdnss, rdnss));
updated = true;
}
SET_FOREACH(dnssl, link->ndisc_dnssl)
- if (dnssl->marked && IN6_ARE_ADDR_EQUAL(&dnssl->router, router)) {
+ if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) {
free(set_remove(link->ndisc_dnssl, dnssl));
updated = true;
}
@@ -304,15 +306,9 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
link->ndisc_routes_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_error_errno(link, m, r, "Could not set NDisc route");
- link_enter_failed(link);
- return 1;
- }
+ r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route");
+ if (r <= 0)
+ return r;
if (link->ndisc_routes_messages == 0) {
log_link_debug(link, "NDisc routes set.");
@@ -330,24 +326,23 @@ static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
return 1;
}
-static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt) {
+static int ndisc_after_route_configure(Request *req, void *object) {
_cleanup_free_ NDiscRoute *nr = NULL;
NDiscRoute *nr_exist;
struct in6_addr router;
- Route *ret;
+ Route *route = object;
+ sd_ndisc_router *rt;
+ Link *link;
int r;
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ROUTE);
+ assert(req->userdata);
assert(route);
- assert(link);
- assert(rt);
-
- r = route_configure(route, link, ndisc_route_handler, &ret);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to set NDisc route: %m");
- if (r > 0)
- link->ndisc_routes_configured = false;
- link->ndisc_routes_messages++;
+ link = req->link;
+ rt = req->userdata;
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
@@ -359,7 +354,7 @@ static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt)
*nr = (NDiscRoute) {
.router = router,
- .route = ret,
+ .route = route,
};
nr_exist = set_get(link->ndisc_routes, nr);
@@ -378,6 +373,39 @@ static int ndisc_route_configure(Route *route, Link *link, sd_ndisc_router *rt)
return 0;
}
+static void ndisc_request_on_free(Request *req) {
+ assert(req);
+
+ sd_ndisc_router_unref(req->userdata);
+}
+
+static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
+ _cleanup_(route_freep) Route *route = in;
+ Request *req;
+ int r;
+
+ assert(route);
+ assert(link);
+ assert(rt);
+
+ r = link_has_route(link, route);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ link->ndisc_routes_configured = false;
+
+ r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages,
+ ndisc_route_handler, &req);
+ if (r < 0)
+ return r;
+
+ req->userdata = sd_ndisc_router_ref(rt);
+ req->after_configure = ndisc_after_route_configure;
+ req->on_free = ndisc_request_on_free;
+
+ return 0;
+}
+
static void ndisc_address_hash_func(const NDiscAddress *x, struct siphash *state) {
address_hash_func(x->address, state);
}
@@ -401,16 +429,9 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
link->ndisc_addresses_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_error_errno(link, m, r, "Could not set NDisc address");
- link_enter_failed(link);
- return 1;
- } else if (r >= 0)
- (void) manager_rtnl_process_address(rtnl, m, link->manager);
+ r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
+ if (r <= 0)
+ return r;
if (link->ndisc_addresses_messages == 0) {
log_link_debug(link, "NDisc SLAAC addresses set.");
@@ -426,24 +447,23 @@ static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
return 1;
}
-static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router *rt) {
+static int ndisc_after_address_configure(Request *req, void *object) {
_cleanup_free_ NDiscAddress *na = NULL;
NDiscAddress *na_exist;
struct in6_addr router;
- Address *ret;
+ sd_ndisc_router *rt;
+ Address *address = object;
+ Link *link;
int r;
+ assert(req);
+ assert(req->link);
+ assert(req->type == REQUEST_TYPE_ADDRESS);
+ assert(req->userdata);
assert(address);
- assert(link);
- assert(rt);
-
- r = address_configure(address, link, ndisc_address_handler, &ret);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m");
- if (r > 0)
- link->ndisc_addresses_configured = false;
- link->ndisc_addresses_messages++;
+ link = req->link;
+ rt = req->userdata;
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
@@ -455,7 +475,7 @@ static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router
*na = (NDiscAddress) {
.router = router,
- .address = ret,
+ .address = address,
};
na_exist = set_get(link->ndisc_addresses, na);
@@ -474,9 +494,33 @@ static int ndisc_address_configure(Address *address, Link *link, sd_ndisc_router
return 0;
}
+static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
+ _cleanup_(address_freep) Address *address = in;
+ Request *req;
+ int r;
+
+ assert(address);
+ assert(link);
+ assert(rt);
+
+ if (address_get(link, address, NULL) < 0)
+ link->ndisc_addresses_configured = false;
+
+ r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages,
+ ndisc_address_handler, &req);
+ if (r < 0)
+ return r;
+
+ req->userdata = sd_ndisc_router_ref(rt);
+ req->after_configure = ndisc_after_address_configure;
+ req->on_free = ndisc_request_on_free;
+
+ return 0;
+}
+
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
- union in_addr_union gateway;
+ struct in6_addr gateway;
uint16_t lifetime;
unsigned preference;
uint32_t table, mtu;
@@ -493,17 +537,17 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
if (lifetime == 0) /* not a default router */
return 0;
- r = sd_ndisc_router_get_address(rt, &gateway.in6);
+ r = sd_ndisc_router_get_address(rt, &gateway);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
- if (link_has_ipv6_address(link, &gateway.in6) > 0) {
+ if (link_has_ipv6_address(link, &gateway) > 0) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buffer = NULL;
- (void) in_addr_to_string(AF_INET6, &gateway, &buffer);
+ (void) in6_addr_to_string(&gateway, &buffer);
log_link_debug(link, "No NDisc route added, gateway %s matches local address",
- strnull(buffer));
+ strna(buffer));
}
return 0;
}
@@ -534,13 +578,13 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
route->protocol = RTPROT_RA;
route->pref = preference;
route->gw_family = AF_INET6;
- route->gw = gateway;
+ route->gw.in6 = gateway;
route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
route->mtu = mtu;
- r = ndisc_route_configure(route, link, rt);
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set default route: %m");
+ return log_link_error_errno(link, r, "Could not request default route: %m");
Route *route_gw;
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
@@ -550,22 +594,26 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
if (route_gw->gw_family != AF_INET6)
continue;
- route_gw->gw = gateway;
- if (!route_gw->table_set)
- route_gw->table = table;
- if (!route_gw->priority_set)
- route_gw->priority = link->network->ipv6_accept_ra_route_metric;
- if (!route_gw->protocol_set)
- route_gw->protocol = RTPROT_RA;
- if (!route_gw->pref_set)
- route_gw->pref = preference;
- route_gw->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
- if (route_gw->mtu == 0)
- route_gw->mtu = mtu;
-
- r = ndisc_route_configure(route_gw, link, rt);
+ r = route_dup(route_gw, &route);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set gateway: %m");
+ return r;
+
+ route->gw.in6 = gateway;
+ if (!route->table_set)
+ route->table = table;
+ if (!route->priority_set)
+ route->priority = link->network->ipv6_accept_ra_route_metric;
+ if (!route->protocol_set)
+ route->protocol = RTPROT_RA;
+ if (!route->pref_set)
+ route->pref = preference;
+ route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
+ if (route->mtu == 0)
+ route->mtu = mtu;
+
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not request gateway: %m");
}
return 0;
@@ -650,7 +698,7 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address,
_cleanup_free_ struct in6_addr *new_address = NULL;
if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
- && (in6_addr_is_null(&j->prefix) || IN6_ARE_ADDR_EQUAL(&j->prefix, address))) {
+ && (in6_addr_is_null(&j->prefix) || in6_addr_equal(&j->prefix, address))) {
/* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop
* does not actually attempt Duplicate Address Detection; the counter will be incremented
* only when the address generation algorithm produces an invalid address, and the loop
@@ -709,7 +757,6 @@ static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address,
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
_cleanup_set_free_free_ Set *addresses = NULL;
- _cleanup_(address_freep) Address *address = NULL;
struct in6_addr addr, *a;
unsigned prefixlen;
usec_t time_now;
@@ -746,18 +793,18 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (r < 0)
return r;
- r = address_new(&address);
- if (r < 0)
- return log_oom();
-
- address->family = AF_INET6;
- address->prefixlen = prefixlen;
- address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
- address->cinfo.ifa_prefered = lifetime_preferred;
-
SET_FOREACH(a, addresses) {
+ _cleanup_(address_freep) Address *address = NULL;
Address *existing_address;
+ r = address_new(&address);
+ if (r < 0)
+ return log_oom();
+
+ address->family = AF_INET6;
+ address->prefixlen = prefixlen;
+ address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+ address->cinfo.ifa_prefered = lifetime_preferred;
address->in_addr.in6 = *a;
/* see RFC4862 section 5.5.3.e */
@@ -778,9 +825,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
if (address->cinfo.ifa_valid == 0)
continue;
- r = ndisc_address_configure(address, link, rt);
+ r = ndisc_request_address(TAKE_PTR(address), link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set SLAAC address: %m");
+ return log_link_error_errno(link, r, "Could not request SLAAC address: %m");
}
return 0;
@@ -824,16 +871,16 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
- r = ndisc_route_configure(route, link, rt);
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set prefix route: %m");;
+ return log_link_error_errno(link, r, "Could not request prefix route: %m");;
return 0;
}
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
- union in_addr_union gateway, dst;
+ struct in6_addr gateway, dst;
uint32_t lifetime;
unsigned preference, prefixlen;
usec_t time_now;
@@ -848,35 +895,35 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
if (lifetime == 0)
return 0;
- r = sd_ndisc_router_route_get_address(rt, &dst.in6);
+ r = sd_ndisc_router_route_get_address(rt, &dst);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route address: %m");
if ((!set_isempty(link->network->ndisc_allow_listed_route_prefix) &&
- !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst.in6)) ||
- set_contains(link->network->ndisc_deny_listed_route_prefix, &dst.in6)) {
+ !set_contains(link->network->ndisc_allow_listed_route_prefix, &dst)) ||
+ set_contains(link->network->ndisc_deny_listed_route_prefix, &dst)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_to_string(AF_INET6, &dst, &buf);
+ (void) in6_addr_to_string(&dst, &buf);
if (!set_isempty(link->network->ndisc_allow_listed_route_prefix))
- log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strnull(buf));
+ log_link_debug(link, "Route prefix '%s' is not in allow list, ignoring", strna(buf));
else
- log_link_debug(link, "Route prefix '%s' is in deny list, ignoring", strnull(buf));
+ log_link_debug(link, "Route prefix '%s' is in deny list, ignoring", strna(buf));
}
return 0;
}
- r = sd_ndisc_router_get_address(rt, &gateway.in6);
+ r = sd_ndisc_router_get_address(rt, &gateway);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
- if (link_has_ipv6_address(link, &gateway.in6) > 0) {
+ if (link_has_ipv6_address(link, &gateway) > 0) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_to_string(AF_INET6, &gateway, &buf);
- log_link_debug(link, "Advertised route gateway, %s, is local to the link, ignoring route", strnull(buf));
+ (void) in6_addr_to_string(&gateway, &buf);
+ log_link_debug(link, "Advertised route gateway %s is local to the link, ignoring route", strna(buf));
}
return 0;
}
@@ -902,15 +949,15 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
route->priority = link->network->ipv6_accept_ra_route_metric;
route->protocol = RTPROT_RA;
route->pref = preference;
- route->gw = gateway;
+ route->gw.in6 = gateway;
route->gw_family = AF_INET6;
- route->dst = dst;
+ route->dst.in6 = dst;
route->dst_prefixlen = prefixlen;
route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
- r = ndisc_route_configure(route, link, rt);
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set additional route: %m");
+ return log_link_error_errno(link, r, "Could not request additional route: %m");
return 0;
}
@@ -959,7 +1006,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
SET_FOREACH(rdnss, link->ndisc_rdnss)
- if (IN6_ARE_ADDR_EQUAL(&rdnss->router, &router))
+ if (in6_addr_equal(&rdnss->router, &router))
rdnss->marked = true;
if (lifetime == 0)
@@ -1053,7 +1100,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
SET_FOREACH(dnssl, link->ndisc_dnssl)
- if (IN6_ARE_ADDR_EQUAL(&dnssl->router, &router))
+ if (in6_addr_equal(&dnssl->router, &router))
dnssl->marked = true;
if (lifetime == 0)
@@ -1119,20 +1166,20 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
switch (type) {
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
- union in_addr_union a;
+ struct in6_addr a;
uint8_t flags;
- r = sd_ndisc_router_prefix_get_address(rt, &a.in6);
+ r = sd_ndisc_router_prefix_get_address(rt, &a);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix address: %m");
if ((!set_isempty(link->network->ndisc_allow_listed_prefix) &&
- !set_contains(link->network->ndisc_allow_listed_prefix, &a.in6)) ||
- set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) {
+ !set_contains(link->network->ndisc_allow_listed_prefix, &a)) ||
+ set_contains(link->network->ndisc_deny_listed_prefix, &a)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *b = NULL;
- (void) in_addr_to_string(AF_INET6, &a, &b);
+ (void) in6_addr_to_string(&a, &b);
if (!set_isempty(link->network->ndisc_allow_listed_prefix))
log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
else
@@ -1187,7 +1234,7 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
- union in_addr_union router;
+ struct in6_addr router;
uint64_t flags;
NDiscAddress *na;
NDiscRoute *nr;
@@ -1198,17 +1245,17 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
assert(link->manager);
assert(rt);
- r = sd_ndisc_router_get_address(rt, &router.in6);
+ r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
if ((!set_isempty(link->network->ndisc_allow_listed_router) &&
- !set_contains(link->network->ndisc_allow_listed_router, &router.in6)) ||
- set_contains(link->network->ndisc_deny_listed_router, &router.in6)) {
+ !set_contains(link->network->ndisc_allow_listed_router, &router)) ||
+ set_contains(link->network->ndisc_deny_listed_router, &router)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
- (void) in_addr_to_string(AF_INET6, &router, &buf);
+ (void) in6_addr_to_string(&router, &buf);
if (!set_isempty(link->network->ndisc_allow_listed_router))
log_link_debug(link, "Router '%s' is not in allow list, ignoring", strna(buf));
else
@@ -1218,11 +1265,11 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
}
SET_FOREACH(na, link->ndisc_addresses)
- if (IN6_ARE_ADDR_EQUAL(&na->router, &router.in6))
+ if (in6_addr_equal(&na->router, &router))
na->marked = true;
SET_FOREACH(nr, link->ndisc_routes)
- if (IN6_ARE_ADDR_EQUAL(&nr->router, &router.in6))
+ if (in6_addr_equal(&nr->router, &router))
nr->marked = true;
r = sd_ndisc_router_get_flags(rt, &flags);
@@ -1235,11 +1282,11 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
- r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
+ r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED));
else
/* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode
* even if router does not have M or O flag. */
- r = dhcp6_request_address(link, false);
+ r = dhcp6_request_information(link, false);
if (r < 0 && r != -EBUSY)
return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
else
@@ -1267,11 +1314,10 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return r;
- if (link->ndisc_addresses_configured && link->ndisc_routes_configured)
- link_check_ready(link);
- else
+ if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured)
link_set_state(link, LINK_STATE_CONFIGURING);
+ link_check_ready(link);
return 0;
}
diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c
index b33f560b79..2afe71c5f8 100644
--- a/src/network/networkd-neighbor.c
+++ b/src/network/networkd-neighbor.c
@@ -7,6 +7,7 @@
#include "networkd-manager.h"
#include "networkd-neighbor.h"
#include "networkd-network.h"
+#include "networkd-queue.h"
#include "set.h"
Neighbor *neighbor_free(Neighbor *neighbor) {
@@ -235,34 +236,12 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const
"%s neighbor: lladdr: %s, dst: %s",
str, strna(lladdr), strna(dst));
}
+static int neighbor_configure(
+ const Neighbor *neighbor,
+ Link *link,
+ link_netlink_message_handler_t callback,
+ Neighbor **ret) {
-static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(m);
- assert(link);
- assert(link->neighbor_messages > 0);
-
- link->neighbor_messages--;
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST)
- /* Neighbor may not exist yet. So, do not enter failed state here. */
- log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring");
-
- if (link->neighbor_messages == 0) {
- log_link_debug(link, "Neighbors set");
- link->neighbors_configured = true;
- link_check_ready(link);
- }
-
- return 1;
-}
-
-static int neighbor_configure(Neighbor *neighbor, Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -271,6 +250,7 @@ static int neighbor_configure(Neighbor *neighbor, Link *link) {
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
+ assert(callback);
log_neighbor_debug(neighbor, "Configuring", link);
@@ -291,22 +271,65 @@ static int neighbor_configure(Neighbor *neighbor, Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m");
- r = netlink_call_async(link->manager->rtnl, NULL, req, neighbor_configure_handler,
+ r = neighbor_add(link, neighbor, ret);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not add neighbor: %m");
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
- link->neighbor_messages++;
link_ref(link);
- r = neighbor_add(link, neighbor, NULL);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not add neighbor: %m");
-
return r;
}
-int link_set_neighbors(Link *link) {
+static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(link->static_neighbor_messages > 0);
+
+ link->static_neighbor_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_warning_errno(link, m, r, "Could not set neighbor");
+ link_enter_failed(link);
+ return 1;
+ }
+
+ if (link->static_neighbor_messages == 0) {
+ log_link_debug(link, "Neighbors set");
+ link->static_neighbors_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
+}
+
+static int link_request_neighbor(
+ Link *link,
+ Neighbor *neighbor,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ assert(link);
+ assert(neighbor);
+
+ log_neighbor_debug(neighbor, "Requesting", link);
+ return link_queue_request(link, REQUEST_TYPE_NEIGHBOR, neighbor, consume_object,
+ message_counter, netlink_handler, ret);
+}
+
+int link_request_static_neighbors(Link *link) {
Neighbor *neighbor;
int r;
@@ -314,24 +337,20 @@ int link_set_neighbors(Link *link) {
assert(link->network);
assert(link->state != _LINK_STATE_INVALID);
- if (link->neighbor_messages != 0) {
- log_link_debug(link, "Neighbors are configuring.");
- return 0;
- }
-
- link->neighbors_configured = false;
+ link->static_neighbors_configured = false;
HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) {
- r = neighbor_configure(neighbor, link);
+ r = link_request_neighbor(link, neighbor, false, &link->static_neighbor_messages,
+ static_neighbor_configure_handler, NULL);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not set neighbor: %m");
+ return log_link_warning_errno(link, r, "Could not request neighbor: %m");
}
- if (link->neighbor_messages == 0) {
- link->neighbors_configured = true;
+ if (link->static_neighbor_messages == 0) {
+ link->static_neighbors_configured = true;
link_check_ready(link);
} else {
- log_link_debug(link, "Setting neighbors");
+ log_link_debug(link, "Requesting neighbors");
link_set_state(link, LINK_STATE_CONFIGURING);
}
@@ -343,6 +362,9 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
assert(m);
assert(link);
+ assert(link->neighbor_remove_messages > 0);
+
+ link->neighbor_remove_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
@@ -382,6 +404,7 @@ static int neighbor_remove(Neighbor *neighbor, Link *link) {
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
+ link->neighbor_remove_messages++;
return 0;
}
@@ -437,6 +460,34 @@ int link_drop_neighbors(Link *link) {
return r;
}
+int request_process_neighbor(Request *req) {
+ Neighbor *ret;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->neighbor);
+ assert(req->type == REQUEST_TYPE_NEIGHBOR);
+
+ if (!link_is_ready_to_configure(req->link, false))
+ return 0;
+
+ if (req->link->neighbor_remove_messages > 0)
+ return 0;
+
+ r = neighbor_configure(req->neighbor, req->link, req->netlink_handler, &ret);
+ if (r < 0)
+ return r;
+
+ if (req->after_configure) {
+ r = req->after_configure(req, ret);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(neighbor_freep) Neighbor *tmp = NULL;
_cleanup_free_ void *lladdr = NULL;
diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h
index 8ad790be2d..7ac2f90eb3 100644
--- a/src/network/networkd-neighbor.h
+++ b/src/network/networkd-neighbor.h
@@ -10,9 +10,10 @@
#include "in-addr-util.h"
#include "networkd-util.h"
-typedef Manager Manager;
-typedef Network Network;
-typedef Link Link;
+typedef struct Link Link;
+typedef struct Manager Manager;
+typedef struct Network Network;
+typedef struct Request Request;
union lladdr_union {
struct ether_addr mac;
@@ -34,10 +35,12 @@ Neighbor *neighbor_free(Neighbor *neighbor);
void network_drop_invalid_neighbors(Network *network);
-int link_set_neighbors(Link *link);
int link_drop_neighbors(Link *link);
int link_drop_foreign_neighbors(Link *link);
+int link_request_static_neighbors(Link *link);
+int request_process_neighbor(Request *req);
+
int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index d7c216af6e..c094065a83 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -228,7 +228,7 @@ struct Network {
bool dhcp6_pd_manage_temporary_address;
int64_t dhcp6_pd_subnet_id;
uint32_t dhcp6_pd_route_metric;
- union in_addr_union dhcp6_pd_token;
+ struct in6_addr dhcp6_pd_token;
/* Bridge Support */
int use_bpdu;
diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c
index f3dbd98ed1..5c6c58356c 100644
--- a/src/network/networkd-nexthop.c
+++ b/src/network/networkd-nexthop.c
@@ -10,6 +10,8 @@
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-nexthop.h"
+#include "networkd-queue.h"
+#include "networkd-route.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
@@ -356,13 +358,16 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s
str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole));
}
-static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
+ assert(link);
+ assert(link->nexthop_remove_messages > 0);
+
+ link->nexthop_remove_messages--;
- /* Note that link may be NULL. */
- if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
@@ -372,6 +377,22 @@ static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
return 1;
}
+static int manager_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
+ int r;
+
+ assert(m);
+ assert(manager);
+ assert(manager->nexthop_remove_messages > 0);
+
+ manager->nexthop_remove_messages--;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -ENOENT)
+ log_message_warning_errno(m, r, "Could not drop nexthop, ignoring");
+
+ return 1;
+}
+
static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
@@ -396,55 +417,40 @@ static int nexthop_remove(const NextHop *nexthop, Manager *manager, Link *link)
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
- r = netlink_call_async(manager->rtnl, NULL, req, nexthop_remove_handler,
- link_netlink_destroy_callback, link);
+ if (link)
+ r = netlink_call_async(manager->rtnl, NULL, req, link_nexthop_remove_handler,
+ link_netlink_destroy_callback, link);
+ else
+ r = netlink_call_async(manager->rtnl, NULL, req, manager_nexthop_remove_handler,
+ NULL, manager);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link); /* link may be NULL, link_ref() is OK with that */
+ if (link)
+ link->nexthop_remove_messages++;
+ else
+ manager->nexthop_remove_messages++;
+
return 0;
}
-static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(link);
- assert(link->nexthop_messages > 0);
-
- link->nexthop_messages--;
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set nexthop");
- link_enter_failed(link);
- return 1;
- }
-
- if (link->nexthop_messages == 0) {
- log_link_debug(link, "Nexthops set");
- link->static_nexthops_configured = true;
- /* Now all nexthops are configured. Let's configure remaining routes. */
- r = link_set_routes_with_gateway(link);
- if (r < 0)
- link_enter_failed(link);
- }
+static int nexthop_configure(
+ const NextHop *nexthop,
+ Link *link,
+ link_netlink_message_handler_t callback,
+ NextHop **ret) {
- return 1;
-}
-
-static int nexthop_configure(const NextHop *nexthop, Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- int r;
+ int r, k;
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
+ assert(callback);
log_nexthop_debug(nexthop, nexthop->id, "Configuring", link);
@@ -482,60 +488,90 @@ static int nexthop_configure(const NextHop *nexthop, Link *link) {
}
}
- r = netlink_call_async(link->manager->rtnl, NULL, req, nexthop_handler,
+ k = nexthop_add(link, nexthop, ret);
+ if (k < 0)
+ return log_link_error_errno(link, k, "Could not add nexthop: %m");
+
+ r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
- r = nexthop_add(link, nexthop, NULL);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not add nexthop: %m");
+ return k;
+}
- return r;
+static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+ assert(link->static_nexthop_messages > 0);
+
+ link->static_nexthop_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_warning_errno(link, m, r, "Could not set nexthop");
+ link_enter_failed(link);
+ return 1;
+ }
+
+ if (link->static_nexthop_messages == 0) {
+ log_link_debug(link, "Nexthops set");
+ link->static_nexthops_configured = true;
+ link_check_ready(link);
+ }
+
+ return 1;
}
-int link_set_nexthops(Link *link) {
- enum {
- PHASE_ID, /* First phase: Nexthops with ID */
- PHASE_WITHOUT_ID, /* Second phase: Nexthops without ID */
- _PHASE_MAX,
- } phase;
+static int link_request_nexthop(
+ Link *link,
+ NextHop *nexthop,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ assert(link);
+ assert(nexthop);
+
+ log_nexthop_debug(nexthop, nexthop->id, "Requesting", link);
+ return link_queue_request(link, REQUEST_TYPE_NEXTHOP, nexthop, consume_object,
+ message_counter, netlink_handler, ret);
+}
+
+int link_request_static_nexthops(Link *link, bool only_ipv4) {
NextHop *nh;
int r;
assert(link);
assert(link->network);
- if (link->nexthop_messages != 0) {
- log_link_debug(link, "Nexthops are configuring.");
- return 0;
- }
-
link->static_nexthops_configured = false;
- for (phase = PHASE_ID; phase < _PHASE_MAX; phase++)
- HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
- if ((nh->id > 0) != (phase == PHASE_ID))
- continue;
-
- r = nexthop_configure(nh, link);
- if (r < 0)
- return log_link_warning_errno(link, r, "Could not set nexthop: %m");
+ HASHMAP_FOREACH(nh, link->network->nexthops_by_section) {
+ if (only_ipv4 && nh->family != AF_INET)
+ continue;
- link->nexthop_messages++;
- }
+ r = link_request_nexthop(link, nh, false, &link->static_nexthop_messages,
+ static_nexthop_handler, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not request nexthop: %m");
+ }
- if (link->nexthop_messages == 0) {
+ if (link->static_nexthop_messages == 0) {
link->static_nexthops_configured = true;
- /* Finally, configure routes with gateways. */
- return link_set_routes_with_gateway(link);
+ link_check_ready(link);
+ } else {
+ log_link_debug(link, "Requesting nexthops");
+ link_set_state(link, LINK_STATE_CONFIGURING);
}
- log_link_debug(link, "Setting nexthops");
- link_set_state(link, LINK_STATE_CONFIGURING);
-
return 0;
}
@@ -657,6 +693,73 @@ int link_drop_nexthops(Link *link) {
return r;
}
+static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
+ assert(link);
+ assert(nexthop);
+
+ if (nexthop->blackhole) {
+ if (link->manager->nexthop_remove_messages > 0)
+ return false;
+ } else {
+ Link *l;
+
+ HASHMAP_FOREACH(l, link->manager->links) {
+ if (l->address_remove_messages > 0)
+ return false;
+ if (l->nexthop_remove_messages > 0)
+ return false;
+ if (l->route_remove_messages > 0)
+ return false;
+ }
+ }
+
+ if (nexthop->id == 0) {
+ Request *req;
+
+ ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+ if (req->type != REQUEST_TYPE_NEXTHOP)
+ continue;
+ if (req->nexthop->id != 0)
+ return false; /* first configure nexthop with id. */
+ }
+ }
+
+ if (nexthop->onlink <= 0 &&
+ in_addr_is_set(nexthop->family, &nexthop->gw) &&
+ !manager_address_is_reachable(link->manager, nexthop->family, &nexthop->gw))
+ return false;
+
+ return true;
+}
+
+int request_process_nexthop(Request *req) {
+ NextHop *ret;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->nexthop);
+ assert(req->type == REQUEST_TYPE_NEXTHOP);
+
+ if (!link_is_ready_to_configure(req->link, false))
+ return 0;
+
+ if (!nexthop_is_ready_to_configure(req->link, req->nexthop))
+ return 0;
+
+ r = nexthop_configure(req->nexthop, req->link, req->netlink_handler, &ret);
+ if (r < 0)
+ return r;
+
+ if (req->after_configure) {
+ r = req->after_configure(req, ret);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(nexthop_freep) NextHop *tmp = NULL;
NextHop *nexthop = NULL;
diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h
index cf06b7e86b..b2e7b45366 100644
--- a/src/network/networkd-nexthop.h
+++ b/src/network/networkd-nexthop.h
@@ -15,6 +15,7 @@
typedef struct Link Link;
typedef struct Manager Manager;
typedef struct Network Network;
+typedef struct Request Request;
typedef struct NextHop {
Network *network;
@@ -36,10 +37,12 @@ NextHop *nexthop_free(NextHop *nexthop);
void network_drop_invalid_nexthops(Network *network);
-int link_set_nexthops(Link *link);
int link_drop_nexthops(Link *link);
int link_drop_foreign_nexthops(Link *link);
+int link_request_static_nexthops(Link *link, bool only_ipv4);
+int request_process_nexthop(Request *req);
+
int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret);
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c
new file mode 100644
index 0000000000..3b9d17651e
--- /dev/null
+++ b/src/network/networkd-queue.c
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "networkd-address.h"
+#include "networkd-manager.h"
+#include "networkd-neighbor.h"
+#include "networkd-nexthop.h"
+#include "networkd-route.h"
+#include "networkd-routing-policy-rule.h"
+#include "networkd-queue.h"
+
+static void request_free_object(RequestType type, void *object) {
+ switch(type) {
+ case REQUEST_TYPE_ADDRESS:
+ address_free(object);
+ break;
+ case REQUEST_TYPE_NEIGHBOR:
+ neighbor_free(object);
+ break;
+ case REQUEST_TYPE_NEXTHOP:
+ nexthop_free(object);
+ break;
+ case REQUEST_TYPE_ROUTE:
+ route_free(object);
+ break;
+ case REQUEST_TYPE_ROUTING_POLICY_RULE:
+ routing_policy_rule_free(object);
+ break;
+ default:
+ assert_not_reached("invalid request type.");
+ }
+}
+
+Request *request_free(Request *req) {
+ if (!req)
+ return NULL;
+
+ if (req->on_free)
+ req->on_free(req);
+ if (req->consume_object)
+ request_free_object(req->type, req->object);
+ if (req->link && req->link->manager)
+ ordered_set_remove(req->link->manager->request_queue, req);
+ link_unref(req->link);
+
+ return mfree(req);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free);
+
+void request_drop(Request *req) {
+ if (req->message_counter)
+ (*req->message_counter)--;
+
+ request_free(req);
+}
+
+int link_queue_request(
+ Link *link,
+ RequestType type,
+ void *object,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ _cleanup_(request_freep) Request *req = NULL;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(type >= 0 && type < _REQUEST_TYPE_MAX);
+ assert(object);
+ assert(netlink_handler);
+
+ req = new(Request, 1);
+ if (!req) {
+ if (consume_object)
+ request_free_object(type, object);
+ return -ENOMEM;
+ }
+
+ *req = (Request) {
+ .link = link,
+ .type = type,
+ .object = object,
+ .consume_object = consume_object,
+ .message_counter = message_counter,
+ .netlink_handler = netlink_handler,
+ };
+
+ link_ref(link);
+
+ r = ordered_set_ensure_put(&link->manager->request_queue, NULL, req);
+ if (r < 0)
+ return r;
+
+ if (req->message_counter)
+ (*req->message_counter)++;
+
+ if (ret)
+ *ret = req;
+
+ TAKE_PTR(req);
+ return 0;
+}
+
+int manager_process_requests(sd_event_source *s, void *userdata) {
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ for (;;) {
+ bool processed = false;
+ Request *req;
+
+ ORDERED_SET_FOREACH(req, manager->request_queue) {
+ switch(req->type) {
+ case REQUEST_TYPE_ADDRESS:
+ r = request_process_address(req);
+ break;
+ case REQUEST_TYPE_NEIGHBOR:
+ r = request_process_neighbor(req);
+ break;
+ case REQUEST_TYPE_NEXTHOP:
+ r = request_process_nexthop(req);
+ break;
+ case REQUEST_TYPE_ROUTE:
+ r = request_process_route(req);
+ break;
+ case REQUEST_TYPE_ROUTING_POLICY_RULE:
+ r = request_process_routing_policy_rule(req);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (r < 0)
+ link_enter_failed(req->link);
+ if (r > 0) {
+ ordered_set_remove(manager->request_queue, req);
+ request_free(req);
+ processed = true;
+ }
+ }
+
+ if (!processed)
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h
new file mode 100644
index 0000000000..f8664d087c
--- /dev/null
+++ b/src/network/networkd-queue.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-event.h"
+
+#include "networkd-link.h"
+
+typedef struct Address Address;
+typedef struct Neighbor Neighbor;
+typedef struct NextHop NextHop;
+typedef struct Route Route;
+typedef struct RoutingPolicyRule RoutingPolicyRule;
+
+typedef struct Request Request;
+
+typedef int (*request_after_configure_handler_t)(Request*, void*);
+typedef void (*request_on_free_handler_t)(Request*);
+
+typedef enum RequestType {
+ REQUEST_TYPE_ADDRESS,
+ REQUEST_TYPE_NEIGHBOR,
+ REQUEST_TYPE_NEXTHOP,
+ REQUEST_TYPE_ROUTE,
+ REQUEST_TYPE_ROUTING_POLICY_RULE,
+ _REQUEST_TYPE_MAX,
+ _REQUEST_TYPE_INVALID = -EINVAL,
+} RequestType;
+
+typedef struct Request {
+ Link *link;
+ RequestType type;
+ bool consume_object;
+ union {
+ Address *address;
+ Neighbor *neighbor;
+ NextHop *nexthop;
+ Route *route;
+ RoutingPolicyRule *rule;
+ void *object;
+ };
+ void *userdata;
+ unsigned *message_counter;
+ link_netlink_message_handler_t netlink_handler;
+ request_after_configure_handler_t after_configure;
+ request_on_free_handler_t on_free;
+} Request;
+
+Request *request_free(Request *req);
+void request_drop(Request *req);
+
+int link_queue_request(
+ Link *link,
+ RequestType type,
+ void *object,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret);
+
+int manager_process_requests(sd_event_source *s, void *userdata);
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 8f3d84129a..8707e5c089 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -9,8 +9,8 @@
#include "networkd-manager.h"
#include "networkd-network.h"
#include "networkd-nexthop.h"
+#include "networkd-queue.h"
#include "networkd-route.h"
-#include "networkd-routing-policy-rule.h"
#include "parse-util.h"
#include "socket-netlink.h"
#include "string-table.h"
@@ -288,7 +288,7 @@ Route *route_free(Route *route) {
set_remove(route->manager->routes_foreign, route);
}
- ordered_set_free_free(route->multipath_routes);
+ ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
sd_event_source_unref(route->expire);
@@ -510,6 +510,44 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, c
}
}
+int route_dup(const Route *src, Route **ret) {
+ _cleanup_(route_freep) Route *dest = NULL;
+ MultipathRoute *m;
+ int r;
+
+ assert(src);
+ assert(ret);
+
+ dest = newdup(Route, src, 1);
+ if (!dest)
+ return -ENOMEM;
+
+ /* Unset all pointers */
+ dest->network = NULL;
+ dest->section = NULL;
+ dest->link = NULL;
+ dest->manager = NULL;
+ dest->multipath_routes = NULL;
+ dest->expire = NULL;
+
+ ORDERED_SET_FOREACH(m, src->multipath_routes) {
+ _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
+
+ r = multipath_route_dup(m, &n);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_ensure_put(&dest->multipath_routes, NULL, n);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(n);
+ }
+
+ *ret = TAKE_PTR(dest);
+ return 0;
+}
+
static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
_cleanup_(route_freep) Route *route = NULL;
int r;
@@ -604,13 +642,115 @@ static bool route_type_is_reject(const Route *route) {
return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
}
-static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *m) {
- _cleanup_free_ char *dst = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL,
+int link_has_route(Link *link, const Route *route) {
+ MultipathRoute *m;
+ int r;
+
+ assert(link);
+ assert(route);
+
+ if (route->nexthop_id > 0) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+ NextHop *nh;
+
+ if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
+ return false;
+
+ r = route_new(&tmp);
+ if (r < 0)
+ return r;
+
+ route_copy(tmp, route, NULL, nh);
+
+ if (route_type_is_reject(route) || (nh && nh->blackhole))
+ return route_get(link->manager, NULL, tmp, NULL) >= 0;
+ else
+ return route_get(NULL, link, tmp, NULL) >= 0;
+ }
+
+ if (ordered_set_isempty(route->multipath_routes)) {
+ if (route_type_is_reject(route))
+ return route_get(link->manager, NULL, route, NULL) >= 0;
+ else
+ return route_get(NULL, link, route, NULL) >= 0;
+ }
+
+ ORDERED_SET_FOREACH(m, route->multipath_routes) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+ Link *l;
+
+ if (m->ifname) {
+ r = resolve_interface(&link->manager->rtnl, m->ifname);
+ if (r < 0)
+ return false;
+ m->ifindex = r;
+
+ if (link_get(link->manager, m->ifindex, &l) < 0)
+ return false;
+ } else
+ l = link;
+
+ r = route_new(&tmp);
+ if (r < 0)
+ return r;
+
+ route_copy(tmp, route, m, NULL);
+
+ if (route_get(NULL, l, tmp, NULL) < 0)
+ return false;
+ }
+
+ return true;
+}
+
+static bool route_address_is_reachable(const Route *route, int family, const union in_addr_union *address) {
+ assert(route);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(address);
+
+ if (route->family != family)
+ return false;
+
+ if (!in_addr_is_set(route->family, &route->dst))
+ return false;
+
+ return in_addr_prefix_intersect(
+ route->family,
+ &route->dst,
+ route->dst_prefixlen,
+ address,
+ FAMILY_ADDRESS_SIZE(family) * 8) > 0;
+}
+
+bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address) {
+ Link *link;
+
+ assert(manager);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(address);
+
+ HASHMAP_FOREACH(link, manager->links) {
+ Route *route;
+
+ SET_FOREACH(route, link->routes)
+ if (route_address_is_reachable(route, family, address))
+ return true;
+ SET_FOREACH(route, link->routes_foreign)
+ if (route_address_is_reachable(route, family, address))
+ return true;
+ }
+
+ return false;
+}
+
+static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
+ _cleanup_free_ char *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
*table = NULL, *scope = NULL, *proto = NULL;
+ const char *gw = NULL;
assert(route);
assert(str);
- assert(m);
+ assert(manager);
/* link may be NULL. */
@@ -621,12 +761,32 @@ static void log_route_debug(const Route *route, const char *str, const Link *lin
(void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst);
if (in_addr_is_set(route->family, &route->src))
(void) in_addr_to_string(route->family, &route->src, &src);
- if (in_addr_is_set(route->gw_family, &route->gw))
- (void) in_addr_to_string(route->gw_family, &route->gw, &gw);
+ if (in_addr_is_set(route->gw_family, &route->gw)) {
+ (void) in_addr_to_string(route->gw_family, &route->gw, &gw_alloc);
+ gw = gw_alloc;
+ } else if (route->gateway_from_dhcp_or_ra) {
+ if (route->gw_family == AF_INET)
+ gw = "_dhcp4";
+ else if (route->gw_family == AF_INET6)
+ gw = "_ipv6ra";
+ } else {
+ MultipathRoute *m;
+
+ ORDERED_SET_FOREACH(m, route->multipath_routes) {
+ _cleanup_free_ char *buf = NULL, *joined = NULL;
+ union in_addr_union a = m->gateway.address;
+
+ (void) in_addr_to_string(m->gateway.family, &a, &buf);
+ joined = strjoin(gw_alloc, gw_alloc ? "," : "", strna(buf), m->ifname ? "@" : "", strempty(m->ifname));
+ if (joined)
+ free_and_replace(gw_alloc, joined);
+ }
+ gw = gw_alloc;
+ }
if (in_addr_is_set(route->family, &route->prefsrc))
(void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
(void) route_scope_to_string_alloc(route->scope, &scope);
- (void) manager_get_route_table_to_string(m, route->table, &table);
+ (void) manager_get_route_table_to_string(manager, route->table, &table);
(void) route_protocol_full_to_string_alloc(route->protocol, &proto);
log_link_debug(link,
@@ -743,18 +903,42 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req
return 0;
}
-static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+int link_route_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
int r;
assert(m);
+ assert(link);
+ assert(link->route_remove_messages > 0);
+ assert(error_msg);
- /* Note that link may be NULL. */
- if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
+ link->route_remove_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -ESRCH)
+ log_link_message_warning_errno(link, m, r, error_msg);
+
+ return 1;
+}
+
+static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ return link_route_remove_handler_internal(rtnl, m, link, "Could not drop route, ignoring");
+}
+
+static int manager_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
+ int r;
+
+ assert(m);
+ assert(manager);
+ assert(manager->route_remove_messages > 0);
+
+ manager->route_remove_messages--;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH)
- log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
+ log_message_warning_errno(m, r, "Could not drop route, ignoring");
return 1;
}
@@ -774,7 +958,6 @@ int route_remove(
if (!manager)
manager = link->manager;
- /* link may be NULL! */
log_route_debug(route, "Removing", link, manager);
@@ -803,18 +986,29 @@ int route_remove(
if (r < 0)
return r;
- r = netlink_call_async(manager->rtnl, NULL, req,
- callback ?: route_remove_handler,
- link_netlink_destroy_callback, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+ if (link) {
+ r = netlink_call_async(manager->rtnl, NULL, req,
+ callback ?: link_route_remove_handler,
+ link_netlink_destroy_callback, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+ link_ref(link);
+ link->route_remove_messages++;
+ } else {
+ r = netlink_call_async(manager->rtnl, NULL, req,
+ manager_route_remove_handler,
+ NULL, manager);
+ if (r < 0)
+ return log_error_errno(r, "Could not send rtnetlink message: %m");
- link_ref(link); /* link may be NULL, link_ref() is OK with that */
+ manager->route_remove_messages++;
+ }
return 0;
}
-static bool link_has_route(const Link *link, const Route *route) {
+static bool link_has_static_route(const Link *link, const Route *route) {
Route *net_route;
assert(link);
@@ -830,7 +1024,7 @@ static bool link_has_route(const Link *link, const Route *route) {
return false;
}
-static bool links_have_route(const Manager *manager, const Route *route, const Link *except) {
+static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) {
Link *link;
assert(manager);
@@ -839,7 +1033,7 @@ static bool links_have_route(const Manager *manager, const Route *route, const L
if (link == except)
continue;
- if (link_has_route(link, route))
+ if (link_has_static_route(link, route))
return true;
}
@@ -863,7 +1057,7 @@ static int manager_drop_routes_internal(Manager *manager, bool foreign, const Li
continue;
/* The route will be configured later, or already configured by a link. */
- if (links_have_route(manager, route, except))
+ if (links_have_static_route(manager, route, except))
continue;
/* The existing links do not have the route. Let's drop this now. It may be
@@ -915,7 +1109,7 @@ int link_drop_foreign_routes(Link *link) {
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
continue;
- if (link_has_route(link, route))
+ if (link_has_static_route(link, route))
k = route_add(NULL, link, route, NULL, NULL, NULL);
else
k = route_remove(route, NULL, link, NULL);
@@ -1094,14 +1288,36 @@ static int append_nexthops(const Route *route, sd_netlink_message *req) {
return 0;
}
-int route_configure(
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
+ int r;
+
+ assert(m);
+ assert(link);
+ assert(error_msg);
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 0;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_warning_errno(link, m, r, "Could not set route with gateway");
+ link_enter_failed(link);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int route_configure(
const Route *route,
Link *link,
link_netlink_message_handler_t callback,
- Route **ret) {
+ unsigned *ret_n_routes,
+ Route ***ret_routes) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- Route *nr;
+ _cleanup_free_ Route **routes = NULL;
+ unsigned n_routes = 0; /* avoid false maybe-uninitialized warning */
int r, k;
assert(link);
@@ -1110,6 +1326,7 @@ int route_configure(
assert(link->ifindex > 0);
assert(IN_SET(route->family, AF_INET, AF_INET6));
assert(callback);
+ assert(!!ret_n_routes == !!ret_routes);
if (route_get(link->manager, link, route, NULL) <= 0 &&
set_size(link->routes) >= routes_max())
@@ -1192,21 +1409,36 @@ int route_configure(
if (route->nexthop_id != 0 ||
in_addr_is_set(route->gw_family, &route->gw) ||
ordered_set_isempty(route->multipath_routes)) {
- k = route_add_and_setup_timer(link, route, NULL, &nr);
+
+ if (ret_routes) {
+ n_routes = 1;
+ routes = new(Route*, n_routes);
+ if (!routes)
+ return log_oom();
+ }
+
+ k = route_add_and_setup_timer(link, route, NULL, routes);
if (k < 0)
return k;
} else {
MultipathRoute *m;
+ Route **p;
r = append_nexthops(route, req);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
- assert(!ret);
+ if (ret_routes) {
+ n_routes = ordered_set_size(route->multipath_routes);
+ routes = new(Route*, n_routes);
+ if (!routes)
+ return log_oom();
+ }
k = 0;
+ p = routes;
ORDERED_SET_FOREACH(m, route->multipath_routes) {
- r = route_add_and_setup_timer(link, route, m, NULL);
+ r = route_add_and_setup_timer(link, route, m, ret_routes ? p++ : NULL);
if (r < 0)
return r;
if (r > 0)
@@ -1221,32 +1453,28 @@ int route_configure(
link_ref(link);
- if (ret)
- *ret = nr;
+ if (ret_routes) {
+ *ret_n_routes = n_routes;
+ *ret_routes = TAKE_PTR(routes);
+ }
return k;
}
-static int route_handler_with_gateway(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
- assert(link->route_messages > 0);
+ assert(link->static_route_messages > 0);
- link->route_messages--;
+ link->static_route_messages--;
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set route with gateway");
- link_enter_failed(link);
- return 1;
- }
+ r = route_configure_handler_internal(rtnl, m, link, "Could not set route");
+ if (r <= 0)
+ return r;
- if (link->route_messages == 0) {
- log_link_debug(link, "Routes with gateway set");
+ if (link->static_route_messages == 0) {
+ log_link_debug(link, "Routes set");
link->static_routes_configured = true;
link_check_ready(link);
}
@@ -1254,140 +1482,157 @@ static int route_handler_with_gateway(sd_netlink *rtnl, sd_netlink_message *m, L
return 1;
}
-static int route_handler_without_gateway(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
+int link_request_route(
+ Link *link,
+ Route *route,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
assert(link);
- assert(link->route_messages > 0);
-
- link->route_messages--;
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not set route without gateway");
- link_enter_failed(link);
- return 1;
- }
-
- if (link->route_messages == 0) {
- log_link_debug(link, "Routes set without gateway");
- /* Now, we can talk to gateways, let's configure nexthops. */
- r = link_set_nexthops(link);
- if (r < 0)
- link_enter_failed(link);
- }
-
- return 1;
-}
-
-static bool route_has_gateway(const Route *route) {
+ assert(link->manager);
assert(route);
- if (in_addr_is_set(route->gw_family, &route->gw))
- return true;
-
- if (!ordered_set_isempty(route->multipath_routes))
- return true;
-
- if (route->nexthop_id > 0)
- return true;
-
- return false;
+ log_route_debug(route, "Requesting", link, link->manager);
+ return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object,
+ message_counter, netlink_handler, ret);
}
-static int link_set_routes_internal(Link *link, bool with_gateway) {
- Route *rt;
+int link_request_static_routes(Link *link, bool only_ipv4) {
+ Route *route;
int r;
assert(link);
assert(link->network);
- HASHMAP_FOREACH(rt, link->network->routes_by_section) {
- if (rt->gateway_from_dhcp_or_ra)
+ link->static_routes_configured = false;
+
+ HASHMAP_FOREACH(route, link->network->routes_by_section) {
+ if (route->gateway_from_dhcp_or_ra)
continue;
- if (route_has_gateway(rt) != with_gateway)
+ if (only_ipv4 && route->family != AF_INET)
continue;
- r = route_configure(rt, link, with_gateway ? route_handler_with_gateway : route_handler_without_gateway, NULL);
+ r = link_request_route(link, route, false, &link->static_route_messages,
+ static_route_handler, NULL);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not set routes: %m");
+ return r;
+ }
- link->route_messages++;
+ if (link->static_route_messages == 0) {
+ link->static_routes_configured = true;
+ link_check_ready(link);
+ } else {
+ log_link_debug(link, "Requesting routes");
+ link_set_state(link, LINK_STATE_CONFIGURING);
}
return 0;
}
-int link_set_routes_with_gateway(Link *link) {
+static int route_is_ready_to_configure(const Route *route, Link *link) {
+ MultipathRoute *m;
+ NextHop *nh = NULL;
int r;
+ assert(route);
assert(link);
- assert(link->network);
-
- if (!link_has_carrier(link) && !link->network->configure_without_carrier)
- /* During configuring addresses, the link lost its carrier. As networkd is dropping
- * the addresses now, let's not configure the routes either. */
- return 0;
- /* Finally, add routes that needs a gateway. */
- r = link_set_routes_internal(link, true);
- if (r < 0)
- return r;
+ if (route->nexthop_id > 0 &&
+ manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
+ return false;
- if (link->route_messages == 0) {
- link->static_routes_configured = true;
- link_check_ready(link);
+ if (route_type_is_reject(route) || (nh && nh->blackhole)) {
+ if (nh && link->manager->nexthop_remove_messages > 0)
+ return false;
+ if (link->manager->route_remove_messages > 0)
+ return false;
} else {
- log_link_debug(link, "Setting routes with gateway");
- link_set_state(link, LINK_STATE_CONFIGURING);
+ Link *l;
+
+ HASHMAP_FOREACH(l, link->manager->links) {
+ if (l->address_remove_messages > 0)
+ return false;
+ if (l->nexthop_remove_messages > 0)
+ return false;
+ if (l->route_remove_messages > 0)
+ return false;
+ }
}
- return 0;
-}
+ if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
+ r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6);
+ if (r <= 0)
+ return r;
+ }
-int link_set_routes(Link *link) {
- int r;
+ if (route->gateway_onlink <= 0 &&
+ in_addr_is_set(route->gw_family, &route->gw) > 0 &&
+ !manager_address_is_reachable(link->manager, route->gw_family, &route->gw))
+ return false;
- assert(link);
- assert(link->network);
- assert(link->state != _LINK_STATE_INVALID);
+ ORDERED_SET_FOREACH(m, route->multipath_routes) {
+ union in_addr_union a = m->gateway.address;
- link->static_routes_configured = false;
+ if (route->gateway_onlink <= 0 &&
+ !manager_address_is_reachable(link->manager, m->gateway.family, &a))
+ return false;
- if (!link->addresses_ready)
- return 0;
+ if (m->ifname) {
+ Link *l;
- if (!link_has_carrier(link) && !link->network->configure_without_carrier)
- /* During configuring addresses, the link lost its carrier. As networkd is dropping
- * the addresses now, let's not configure the routes either. */
- return 0;
+ r = resolve_interface(&link->manager->rtnl, m->ifname);
+ if (r < 0)
+ return false;
+ m->ifindex = r;
- if (link->route_messages != 0) {
- log_link_debug(link, "Static routes are configuring.");
- return 0;
+ if (link_get(link->manager, m->ifindex, &l) < 0)
+ return false;
+
+ if (!link_is_ready_to_configure(l, true))
+ return false;
+ }
}
- r = link_set_routing_policy_rules(link);
- if (r < 0)
+ return true;
+}
+
+int request_process_route(Request *req) {
+ _cleanup_free_ Route **routes = NULL;
+ unsigned n_routes;
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->route);
+ assert(req->type == REQUEST_TYPE_ROUTE);
+
+ if (!link_is_ready_to_configure(req->link, false))
+ return 0;
+
+ r = route_is_ready_to_configure(req->route, req->link);
+ if (r <= 0)
return r;
- /* First, add the routes that enable us to talk to gateways. */
- r = link_set_routes_internal(link, false);
+ r = route_configure(req->route, req->link, req->netlink_handler,
+ req->after_configure ? &n_routes : NULL,
+ req->after_configure ? &routes : NULL);
if (r < 0)
return r;
- if (link->route_messages == 0)
- /* If no route is configured, then configure nexthops. */
- return link_set_nexthops(link);
+ if (req->after_configure) {
+ assert(n_routes > 0);
- log_link_debug(link, "Setting routes without gateway");
- link_set_state(link, LINK_STATE_CONFIGURING);
+ for (unsigned i = 0; i < n_routes; i++) {
+ r = req->after_configure(req, routes[i]);
+ if (r < 0)
+ return r;
+ }
+ }
- return 0;
+ return 1;
}
static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
@@ -2460,13 +2705,14 @@ int config_parse_multipath_route(
void *data,
void *userdata) {
+ _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
_cleanup_(route_free_or_set_invalidp) Route *n = NULL;
- _cleanup_free_ char *word = NULL, *buf = NULL;
- _cleanup_free_ MultipathRoute *m = NULL;
+ _cleanup_free_ char *word = NULL;
Network *network = userdata;
- const char *p, *ip, *dev;
union in_addr_union a;
int family, r;
+ const char *p;
+ char *dev;
assert(filename);
assert(section);
@@ -2484,7 +2730,7 @@ int config_parse_multipath_route(
}
if (isempty(rvalue)) {
- n->multipath_routes = ordered_set_free_free(n->multipath_routes);
+ n->multipath_routes = ordered_set_free_with_destructor(n->multipath_routes, multipath_route_free);
return 0;
}
@@ -2504,15 +2750,14 @@ int config_parse_multipath_route(
dev = strchr(word, '@');
if (dev) {
- buf = strndup(word, dev - word);
- if (!buf)
+ *dev++ = '\0';
+
+ m->ifname = strdup(dev);
+ if (!m->ifname)
return log_oom();
- ip = buf;
- dev++;
- } else
- ip = word;
+ }
- r = in_addr_from_string_auto(ip, &family, &a);
+ r = in_addr_from_string_auto(word, &family, &a);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
@@ -2521,16 +2766,6 @@ int config_parse_multipath_route(
m->gateway.address = a;
m->gateway.family = family;
- if (dev) {
- r = resolve_interface(NULL, dev);
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Invalid interface name or index, ignoring assignment: %s", dev);
- return 0;
- }
- m->ifindex = r;
- }
-
if (!isempty(p)) {
r = safe_atou32(p, &m->weight);
if (r < 0) {
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index ddb433817a..bb007dd74a 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -3,7 +3,6 @@
#include <inttypes.h>
#include <stdbool.h>
-#include <stdio.h>
#include "sd-netlink.h"
@@ -14,6 +13,7 @@
typedef struct Manager Manager;
typedef struct Network Network;
+typedef struct Request Request;
typedef struct Route {
Network *network;
@@ -71,18 +71,31 @@ extern const struct hash_ops route_hash_ops;
int route_new(Route **ret);
Route *route_free(Route *route);
DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
+int route_dup(const Route *src, Route **ret);
-int route_configure(const Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
+int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
+int link_route_remove_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
int route_remove(const Route *route, Manager *manager, Link *link, link_netlink_message_handler_t callback);
-int link_set_routes(Link *link);
-int link_set_routes_with_gateway(Link *link);
+int link_has_route(Link *link, const Route *route);
+bool manager_address_is_reachable(Manager *manager, int family, const union in_addr_union *address);
+
int link_drop_routes(Link *link);
int link_drop_foreign_routes(Link *link);
uint32_t link_get_dhcp_route_table(const Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(const Link *link);
+int link_request_route(
+ Link *link,
+ Route *route,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret);
+int link_request_static_routes(Link *link, bool only_ipv4);
+int request_process_route(Request *req);
+
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
int network_add_ipv4ll_route(Network *network);
diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c
index 14e079eed3..b44ee8b627 100644
--- a/src/network/networkd-routing-policy-rule.c
+++ b/src/network/networkd-routing-policy-rule.c
@@ -12,6 +12,8 @@
#include "ip-protocol-list.h"
#include "netlink-util.h"
#include "networkd-manager.h"
+#include "networkd-queue.h"
+#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-util.h"
#include "parse-util.h"
@@ -109,45 +111,35 @@ static int routing_policy_rule_new_static(Network *network, const char *filename
return 0;
}
-static int routing_policy_rule_copy(RoutingPolicyRule *dest, const RoutingPolicyRule *src) {
- _cleanup_free_ char *iif = NULL, *oif = NULL;
+static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRule **ret) {
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *dest = NULL;
- assert(dest);
assert(src);
+ assert(ret);
+
+ dest = newdup(RoutingPolicyRule, src, 1);
+ if (!dest)
+ return -ENOMEM;
+
+ /* Unset all pointers */
+ dest->manager = NULL;
+ dest->network = NULL;
+ dest->section = NULL;
+ dest->iif = dest->oif = NULL;
if (src->iif) {
- iif = strdup(src->iif);
- if (!iif)
+ dest->iif = strdup(src->iif);
+ if (!dest->iif)
return -ENOMEM;
}
if (src->oif) {
- oif = strdup(src->oif);
- if (!oif)
+ dest->oif = strdup(src->oif);
+ if (!dest->oif)
return -ENOMEM;
}
- dest->family = src->family;
- dest->from = src->from;
- dest->from_prefixlen = src->from_prefixlen;
- dest->to = src->to;
- dest->to_prefixlen = src->to_prefixlen;
- dest->invert_rule = src->invert_rule;
- dest->tos = src->tos;
- dest->type = src->type;
- dest->fwmark = src->fwmark;
- dest->fwmask = src->fwmask;
- dest->priority = src->priority;
- dest->table = src->table;
- dest->iif = TAKE_PTR(iif);
- dest->oif = TAKE_PTR(oif);
- dest->ipproto = src->ipproto;
- dest->protocol = src->protocol;
- dest->sport = src->sport;
- dest->dport = src->dport;
- dest->uid_range = src->uid_range;
- dest->suppress_prefixlen = src->suppress_prefixlen;
-
+ *ret = TAKE_PTR(dest);
return 0;
}
@@ -323,7 +315,7 @@ static int routing_policy_rule_get(Manager *m, const RoutingPolicyRule *rule, Ro
return -ENOENT;
}
-static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, int family, RoutingPolicyRule **ret) {
+static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, RoutingPolicyRule **ret) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
RoutingPolicyRule *existing;
bool is_new = false;
@@ -331,19 +323,12 @@ static int routing_policy_rule_add(Manager *m, const RoutingPolicyRule *in, int
assert(m);
assert(in);
- assert(IN_SET(family, AF_INET, AF_INET6));
- assert(in->family == AF_UNSPEC || in->family == family);
+ assert(IN_SET(in->family, AF_INET, AF_INET6));
- r = routing_policy_rule_new(&rule);
+ r = routing_policy_rule_dup(in, &rule);
if (r < 0)
return r;
- r = routing_policy_rule_copy(rule, in);
- if (r < 0)
- return r;
-
- rule->family = family;
-
r = routing_policy_rule_get(m, rule, &existing);
if (r == -ENOENT) {
/* Rule does not exist, use a new one. */
@@ -390,11 +375,11 @@ static int routing_policy_rule_consume_foreign(Manager *m, RoutingPolicyRule *ru
return 1;
}
-static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, int family, const char *str, const Link *link, const Manager *m) {
+static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, const char *str, const Link *link, const Manager *m) {
_cleanup_free_ char *from = NULL, *to = NULL, *table = NULL;
assert(rule);
- assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(IN_SET(rule->family, AF_INET, AF_INET6));
assert(str);
assert(m);
@@ -403,8 +388,8 @@ static void log_routing_policy_rule_debug(const RoutingPolicyRule *rule, int fam
if (!DEBUG_LOGGING)
return;
- (void) in_addr_prefix_to_string(family, &rule->from, rule->from_prefixlen, &from);
- (void) in_addr_prefix_to_string(family, &rule->to, rule->to_prefixlen, &to);
+ (void) in_addr_prefix_to_string(rule->family, &rule->from, rule->from_prefixlen, &from);
+ (void) in_addr_prefix_to_string(rule->family, &rule->to, rule->to_prefixlen, &to);
(void) manager_get_route_table_to_string(m, rule->table, &table);
log_link_debug(link,
@@ -534,10 +519,14 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule
return 0;
}
-static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
+static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
int r;
assert(m);
+ assert(manager);
+ assert(manager->routing_policy_rule_remove_messages > 0);
+
+ manager->routing_policy_rule_remove_messages--;
r = sd_netlink_message_get_errno(m);
if (r < 0)
@@ -555,7 +544,7 @@ static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *ma
assert(manager->rtnl);
assert(IN_SET(rule->family, AF_INET, AF_INET6));
- log_routing_policy_rule_debug(rule, rule->family, "Removing", NULL, manager);
+ log_routing_policy_rule_debug(rule, "Removing", NULL, manager);
r = sd_rtnl_message_new_routing_policy_rule(manager->rtnl, &m, RTM_DELRULE, rule->family);
if (r < 0)
@@ -565,58 +554,37 @@ static int routing_policy_rule_remove(const RoutingPolicyRule *rule, Manager *ma
if (r < 0)
return r;
- r = sd_netlink_call_async(manager->rtnl, NULL, m,
- routing_policy_rule_remove_handler,
- NULL, NULL, 0, __func__);
+ r = netlink_call_async(manager->rtnl, NULL, m,
+ routing_policy_rule_remove_handler,
+ NULL, manager);
if (r < 0)
return log_error_errno(r, "Could not send rtnetlink message: %m");
+ manager->routing_policy_rule_remove_messages++;
+
return 0;
}
-static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(rtnl);
- assert(m);
- assert(link);
- assert(link->ifname);
- assert(link->routing_policy_rule_messages > 0);
-
- link->routing_policy_rule_messages--;
-
- if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
- return 1;
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST) {
- log_link_message_warning_errno(link, m, r, "Could not add routing policy rule");
- link_enter_failed(link);
- return 1;
- }
-
- if (link->routing_policy_rule_messages == 0) {
- log_link_debug(link, "Routing policy rule configured");
- link->routing_policy_rules_configured = true;
- link_check_ready(link);
- }
-
- return 1;
-}
+static int routing_policy_rule_configure(
+ const RoutingPolicyRule *rule,
+ Link *link,
+ link_netlink_message_handler_t callback,
+ RoutingPolicyRule **ret) {
-static int routing_policy_rule_configure_internal(const RoutingPolicyRule *rule, int family, Link *link) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(rule);
+ assert(IN_SET(rule->family, AF_INET, AF_INET6));
assert(link);
assert(link->ifindex > 0);
assert(link->manager);
assert(link->manager->rtnl);
+ assert(callback);
- log_routing_policy_rule_debug(rule, family, "Configuring", link, link->manager);
+ log_routing_policy_rule_debug(rule, "Configuring", link, link->manager);
- r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, family);
+ r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m");
@@ -624,43 +592,20 @@ static int routing_policy_rule_configure_internal(const RoutingPolicyRule *rule,
if (r < 0)
return r;
- r = netlink_call_async(link->manager->rtnl, NULL, m,
- routing_policy_rule_handler,
+ r = routing_policy_rule_add(link->manager, rule, ret);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not add rule: %m");
+
+ r = netlink_call_async(link->manager->rtnl, NULL, m, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
- link->routing_policy_rule_messages++;
-
- r = routing_policy_rule_add(link->manager, rule, family, NULL);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not add rule: %m");
return r;
}
-static int routing_policy_rule_configure(const RoutingPolicyRule *rule, Link *link) {
- int r;
-
- if (IN_SET(rule->family, AF_INET, AF_INET6))
- return routing_policy_rule_configure_internal(rule, rule->family, link);
-
- if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) {
- r = routing_policy_rule_configure_internal(rule, AF_INET, link);
- if (r < 0)
- return r;
- }
-
- if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) {
- r = routing_policy_rule_configure_internal(rule, AF_INET6, link);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyRule *rule, const Link *except) {
Link *link;
int r;
@@ -685,11 +630,7 @@ static int links_have_routing_policy_rule(const Manager *m, const RoutingPolicyR
/* The case Family=both. */
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
- r = routing_policy_rule_new(&tmp);
- if (r < 0)
- return r;
-
- r = routing_policy_rule_copy(tmp, link_rule);
+ r = routing_policy_rule_dup(link_rule, &tmp);
if (r < 0)
return r;
@@ -735,36 +676,153 @@ int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const L
return r;
}
-int link_set_routing_policy_rules(Link *link) {
- RoutingPolicyRule *rule;
+static int static_routing_policy_rule_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
+ assert(rtnl);
+ assert(m);
assert(link);
- assert(link->network);
+ assert(link->ifname);
+ assert(link->static_routing_policy_rule_messages > 0);
- if (link->routing_policy_rule_messages != 0) {
- log_link_debug(link, "Routing policy rules are configuring.");
- return 0;
+ link->static_routing_policy_rule_messages--;
+
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST) {
+ log_link_message_warning_errno(link, m, r, "Could not add routing policy rule");
+ link_enter_failed(link);
+ return 1;
+ }
+
+ if (link->static_routing_policy_rule_messages == 0) {
+ log_link_debug(link, "Routing policy rule configured");
+ link->static_routing_policy_rules_configured = true;
+ link_check_ready(link);
}
- link->routing_policy_rules_configured = false;
+ return 1;
+}
+
+static int link_request_routing_policy_rule(
+ Link *link,
+ RoutingPolicyRule *rule,
+ bool consume_object,
+ unsigned *message_counter,
+ link_netlink_message_handler_t netlink_handler,
+ Request **ret) {
+
+ assert(link);
+ assert(link->manager);
+ assert(rule);
+
+ log_routing_policy_rule_debug(rule, "Requesting", link, link->manager);
+ return link_queue_request(link, REQUEST_TYPE_ROUTING_POLICY_RULE, rule, consume_object,
+ message_counter, netlink_handler, ret);
+}
+
+static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule *rule) {
+ int r;
+
+ if (IN_SET(rule->family, AF_INET, AF_INET6))
+ return link_request_routing_policy_rule(link, rule, false,
+ &link->static_routing_policy_rule_messages,
+ static_routing_policy_rule_configure_handler,
+ NULL);
+
+ if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV4)) {
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+ r = routing_policy_rule_dup(rule, &tmp);
+ if (r < 0)
+ return r;
+
+ tmp->family = AF_INET;
+
+ r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true,
+ &link->static_routing_policy_rule_messages,
+ static_routing_policy_rule_configure_handler,
+ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ if (FLAGS_SET(rule->address_family, ADDRESS_FAMILY_IPV6)) {
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
+
+ r = routing_policy_rule_dup(rule, &tmp);
+ if (r < 0)
+ return r;
+
+ tmp->family = AF_INET6;
+
+ r = link_request_routing_policy_rule(link, TAKE_PTR(tmp), true,
+ &link->static_routing_policy_rule_messages,
+ static_routing_policy_rule_configure_handler,
+ NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int link_request_static_routing_policy_rules(Link *link) {
+ RoutingPolicyRule *rule;
+ int r;
+
+ assert(link);
+ assert(link->network);
+
+ link->static_routing_policy_rules_configured = false;
HASHMAP_FOREACH(rule, link->network->rules_by_section) {
- r = routing_policy_rule_configure(rule, link);
+ r = link_request_static_routing_policy_rule(link, rule);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not set routing policy rule: %m");
+ return log_link_warning_errno(link, r, "Could not request routing policy rule: %m");
}
- if (link->routing_policy_rule_messages == 0)
- link->routing_policy_rules_configured = true;
- else {
- log_link_debug(link, "Setting routing policy rules");
+ if (link->static_routing_policy_rule_messages == 0) {
+ link->static_routing_policy_rules_configured = true;
+ link_check_ready(link);
+ } else {
+ log_link_debug(link, "Requesting routing policy rules");
link_set_state(link, LINK_STATE_CONFIGURING);
}
return 0;
}
+int request_process_routing_policy_rule(Request *req) {
+ RoutingPolicyRule *ret = NULL; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(req);
+ assert(req->link);
+ assert(req->rule);
+ assert(req->type == REQUEST_TYPE_ROUTING_POLICY_RULE);
+
+ if (!link_is_ready_to_configure(req->link, false))
+ return 0;
+
+ if (req->link->manager->routing_policy_rule_remove_messages > 0)
+ return 0;
+
+ r = routing_policy_rule_configure(req->rule, req->link, req->netlink_handler, &ret);
+ if (r < 0)
+ return r;
+
+ if (req->after_configure) {
+ r = req->after_configure(req, ret);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
static const RoutingPolicyRule kernel_rules[] = {
{ .family = AF_INET, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
{ .family = AF_INET, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, },
@@ -977,11 +1035,11 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
switch (type) {
case RTM_NEWRULE:
if (rule)
- log_routing_policy_rule_debug(tmp, tmp->family, "Received remembered", NULL, m);
+ log_routing_policy_rule_debug(tmp, "Received remembered", NULL, m);
else if (!m->manage_foreign_routes)
- log_routing_policy_rule_debug(tmp, tmp->family, "Ignoring received foreign", NULL, m);
+ log_routing_policy_rule_debug(tmp, "Ignoring received foreign", NULL, m);
else {
- log_routing_policy_rule_debug(tmp, tmp->family, "Remembering foreign", NULL, m);
+ log_routing_policy_rule_debug(tmp, "Remembering foreign", NULL, m);
r = routing_policy_rule_consume_foreign(m, TAKE_PTR(tmp));
if (r < 0)
log_warning_errno(r, "Could not remember foreign rule, ignoring: %m");
@@ -989,10 +1047,10 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
break;
case RTM_DELRULE:
if (rule) {
- log_routing_policy_rule_debug(tmp, tmp->family, "Forgetting", NULL, m);
+ log_routing_policy_rule_debug(tmp, "Forgetting", NULL, m);
routing_policy_rule_free(rule);
} else
- log_routing_policy_rule_debug(tmp, tmp->family, "Kernel removed unknown", NULL, m);
+ log_routing_policy_rule_debug(tmp, "Kernel removed unknown", NULL, m);
break;
default:
diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h
index dbda8c51b0..69b0535659 100644
--- a/src/network/networkd-routing-policy-rule.h
+++ b/src/network/networkd-routing-policy-rule.h
@@ -4,16 +4,15 @@
#include <inttypes.h>
#include <linux/fib_rules.h>
#include <stdbool.h>
-#include <stdio.h>
#include "conf-parser.h"
#include "in-addr-util.h"
#include "networkd-util.h"
-#include "set.h"
-typedef struct Network Network;
typedef struct Link Link;
typedef struct Manager Manager;
+typedef struct Network Network;
+typedef struct Request Request;
typedef struct RoutingPolicyRule {
Manager *manager;
@@ -55,7 +54,8 @@ RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule);
void network_drop_invalid_routing_policy_rules(Network *network);
-int link_set_routing_policy_rules(Link *link);
+int link_request_static_routing_policy_rules(Link *link);
+int request_process_routing_policy_rule(Request *req);
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except);
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 1e50a75980..97581d2e59 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -2784,20 +2784,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
expect_up = initial_up
next_up = not expect_up
- # if initial expected state is down, must wait for setup_state to reach configuring
- # so systemd-networkd considers it 'activated'
- setup_state = None if initial_up else 'configuring'
-
for iteration in range(4):
with self.subTest(iteration=iteration, expect_up=expect_up):
operstate = 'routable' if expect_up else 'off'
+ setup_state = 'configured' if expect_up else None
self.wait_operstate('test1', operstate, setup_state=setup_state, setup_timeout=20)
- setup_state = None
if expect_up:
self.assertIn('UP', check_output('ip link show test1'))
self.assertIn('192.168.10.30/24', check_output('ip address show test1'))
- self.assertIn('default via 192.168.10.1', check_output('ip route show'))
+ self.assertIn('default via 192.168.10.1', check_output('ip route show dev test1'))
else:
self.assertIn('DOWN', check_output('ip link show test1'))
@@ -2900,6 +2896,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertEqual(rc, 0)
time.sleep(1)
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
output = check_output('ip nexthop list dev veth99')
print(output)
self.assertEqual(output, '')
@@ -2913,6 +2911,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertEqual(rc, 0)
time.sleep(1)
+ self.wait_online(['veth99:routable', 'veth-peer:routable'])
+
rc = call('ip link del veth99')
self.assertEqual(rc, 0)
time.sleep(2)