summaryrefslogtreecommitdiff
path: root/src/network/networkd-nexthop.c
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-02-15 10:00:14 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-02-23 22:47:11 +0900
commit25b82b6e0ef02e3c0b58a3f8e253ae3236c2b72b (patch)
tree1eb0689aff5c8b2e1c3a2935b39d7bc3510512e5 /src/network/networkd-nexthop.c
parent0e9d129c1603a0768adcb4e6b8218c16a07ac82f (diff)
downloadsystemd-25b82b6e0ef02e3c0b58a3f8e253ae3236c2b72b.tar.gz
network: nexthop: drop unnecessary nexthops
Similar to addresses or routes, this makes networkd drops unnecessary nexthops on configuring links or when a link is dropped.
Diffstat (limited to 'src/network/networkd-nexthop.c')
-rw-r--r--src/network/networkd-nexthop.c178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c
index 29ed5b5ef9..470095c897 100644
--- a/src/network/networkd-nexthop.c
+++ b/src/network/networkd-nexthop.c
@@ -151,6 +151,16 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
nexthop_compare_func,
nexthop_free);
+static bool nexthop_equal(const NextHop *a, const NextHop *b) {
+ if (a == b)
+ return true;
+
+ if (!a || !b)
+ return false;
+
+ return nexthop_compare_func(a, b) == 0;
+}
+
static void nexthop_copy(NextHop *dest, const NextHop *src) {
assert(dest);
assert(src);
@@ -345,6 +355,56 @@ static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *s
}
}
+static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(m);
+
+ /* Note that link may be NULL. */
+ if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -ENOENT)
+ log_link_message_warning_errno(link, 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;
+
+ assert(nexthop);
+ assert(manager);
+
+ /* link may be NULL. */
+
+ if (nexthop->id == 0) {
+ log_link_debug(link, "Cannot remove nexthop without valid ID, ignoring.");
+ return 0;
+ }
+
+ log_nexthop_debug(nexthop, nexthop->id, "Removing", link);
+
+ r = sd_rtnl_message_new_nexthop(manager->rtnl, &req, RTM_DELNEXTHOP, AF_UNSPEC, RTPROT_UNSPEC);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
+
+ r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
+ 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 (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 */
+
+ return 0;
+}
+
static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@@ -478,6 +538,124 @@ int link_set_nexthops(Link *link) {
return 0;
}
+static bool link_has_nexthop(const Link *link, const NextHop *nexthop) {
+ NextHop *net_nexthop;
+
+ assert(link);
+ assert(nexthop);
+
+ if (!link->network)
+ return false;
+
+ HASHMAP_FOREACH(net_nexthop, link->network->nexthops_by_section)
+ if (nexthop_equal(net_nexthop, nexthop))
+ return true;
+
+ return false;
+}
+
+static bool links_have_nexthop(const Manager *manager, const NextHop *nexthop, const Link *except) {
+ Link *link;
+
+ assert(manager);
+
+ HASHMAP_FOREACH(link, manager->links) {
+ if (link == except)
+ continue;
+
+ if (link_has_nexthop(link, nexthop))
+ return true;
+ }
+
+ return false;
+}
+
+static int manager_drop_nexthops_internal(Manager *manager, bool foreign, const Link *except) {
+ NextHop *nexthop;
+ Set *nexthops;
+ int k, r = 0;
+
+ assert(manager);
+
+ nexthops = foreign ? manager->nexthops_foreign : manager->nexthops;
+ SET_FOREACH(nexthop, nexthops) {
+ /* do not touch nexthop created by the kernel */
+ if (nexthop->protocol == RTPROT_KERNEL)
+ continue;
+
+ /* The nexthop will be configured later, or already configured by a link. */
+ if (links_have_nexthop(manager, nexthop, except))
+ continue;
+
+ /* The existing links do not have the nexthop. Let's drop this now. It may be
+ * re-configured later. */
+ k = nexthop_remove(nexthop, manager, NULL);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static int manager_drop_foreign_nexthops(Manager *manager) {
+ return manager_drop_nexthops_internal(manager, true, NULL);
+}
+
+static int manager_drop_nexthops(Manager *manager, const Link *except) {
+ return manager_drop_nexthops_internal(manager, false, except);
+}
+
+int link_drop_foreign_nexthops(Link *link) {
+ NextHop *nexthop;
+ int k, r = 0;
+
+ assert(link);
+ assert(link->manager);
+
+ SET_FOREACH(nexthop, link->nexthops_foreign) {
+ /* do not touch nexthop created by the kernel */
+ if (nexthop->protocol == RTPROT_KERNEL)
+ continue;
+
+ if (link_has_nexthop(link, nexthop))
+ k = nexthop_add(link, nexthop, NULL);
+ else
+ k = nexthop_remove(nexthop, link->manager, link);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ k = manager_drop_foreign_nexthops(link->manager);
+ if (k < 0 && r >= 0)
+ r = k;
+
+ return r;
+}
+
+int link_drop_nexthops(Link *link) {
+ NextHop *nexthop;
+ int k, r = 0;
+
+ assert(link);
+ assert(link->manager);
+
+ SET_FOREACH(nexthop, link->nexthops) {
+ /* do not touch nexthop created by the kernel */
+ if (nexthop->protocol == RTPROT_KERNEL)
+ continue;
+
+ k = nexthop_remove(nexthop, link->manager, link);
+ if (k < 0 && r >= 0)
+ r = k;
+ }
+
+ k = manager_drop_nexthops(link->manager, link);
+ if (k < 0 && r >= 0)
+ r = k;
+
+ return r;
+}
+
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(nexthop_freep) NextHop *tmp = NULL;
NextHop *nexthop = NULL;