]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-queue.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / network / networkd-queue.c
index 3b9d17651ecf38739cf4a5ab0485ddceacb6c2c7..dcf5f4f0527cccbe91ecfaf5b70b48cb66e83cd0 100644 (file)
@@ -1,45 +1,80 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "networkd-address.h"
+#include "networkd-address-label.h"
+#include "networkd-bridge-fdb.h"
+#include "networkd-bridge-mdb.h"
+#include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
+#include "networkd-ipv6-proxy-ndp.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"
+#include "networkd-setlink.h"
 
 static void request_free_object(RequestType type, void *object) {
         switch(type) {
+        case REQUEST_TYPE_ACTIVATE_LINK:
+                break;
         case REQUEST_TYPE_ADDRESS:
                 address_free(object);
                 break;
+        case REQUEST_TYPE_ADDRESS_LABEL:
+                address_label_free(object);
+                break;
+        case REQUEST_TYPE_BRIDGE_FDB:
+                bridge_fdb_free(object);
+                break;
+        case REQUEST_TYPE_BRIDGE_MDB:
+                bridge_mdb_free(object);
+                break;
+        case REQUEST_TYPE_DHCP_SERVER:
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
+                break;
+        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                free(object);
+                break;
         case REQUEST_TYPE_NEIGHBOR:
                 neighbor_free(object);
                 break;
         case REQUEST_TYPE_NEXTHOP:
                 nexthop_free(object);
                 break;
+        case REQUEST_TYPE_RADV:
+                break;
         case REQUEST_TYPE_ROUTE:
                 route_free(object);
                 break;
         case REQUEST_TYPE_ROUTING_POLICY_RULE:
                 routing_policy_rule_free(object);
                 break;
+        case REQUEST_TYPE_SET_LINK:
+        case REQUEST_TYPE_STACKED_NETDEV:
+        case REQUEST_TYPE_UP_DOWN:
+                break;
         default:
-                assert_not_reached("invalid request type.");
+                assert_not_reached();
         }
 }
 
-Request *request_free(Request *req) {
+static Request *request_free(Request *req) {
         if (!req)
                 return NULL;
 
+        if (req->link && req->link->manager)
+                /* To prevent from triggering assertions in hash functions, remove this request before
+                 * freeing object below. */
+                ordered_set_remove(req->link->manager->request_queue, req);
         if (req->on_free)
+                /* on_free() may use object. So, let's call this earlier. */
                 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);
@@ -48,12 +83,129 @@ Request *request_free(Request *req) {
 DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free);
 
 void request_drop(Request *req) {
+        if (!req)
+                return;
+
         if (req->message_counter)
                 (*req->message_counter)--;
 
         request_free(req);
 }
 
+static void request_hash_func(const Request *req, struct siphash *state) {
+        assert(req);
+        assert(req->link);
+        assert(state);
+
+        siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
+        siphash24_compress(&req->type, sizeof(req->type), state);
+
+        switch(req->type) {
+        case REQUEST_TYPE_ACTIVATE_LINK:
+                break;
+        case REQUEST_TYPE_ADDRESS:
+                address_hash_func(req->address, state);
+                break;
+        case REQUEST_TYPE_ADDRESS_LABEL:
+        case REQUEST_TYPE_BRIDGE_FDB:
+        case REQUEST_TYPE_BRIDGE_MDB:
+        case REQUEST_TYPE_STACKED_NETDEV:
+                /* TODO: Currently, these types do not have any specific hash and compare functions.
+                 * Fortunately, all these objects are 'static', thus we can use the trivial functions. */
+                trivial_hash_func(req->object, state);
+                break;
+        case REQUEST_TYPE_DHCP_SERVER:
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
+                /* These types do not have an object. */
+                break;
+        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                in6_addr_hash_func(req->ipv6_proxy_ndp, state);
+                break;
+        case REQUEST_TYPE_NEIGHBOR:
+                neighbor_hash_func(req->neighbor, state);
+                break;
+        case REQUEST_TYPE_NEXTHOP:
+                nexthop_hash_func(req->nexthop, state);
+                break;
+        case REQUEST_TYPE_RADV:
+                /* This type does not have an object. */
+                break;
+        case REQUEST_TYPE_ROUTE:
+                route_hash_func(req->route, state);
+                break;
+        case REQUEST_TYPE_ROUTING_POLICY_RULE:
+                routing_policy_rule_hash_func(req->rule, state);
+                break;
+        case REQUEST_TYPE_SET_LINK: {
+                trivial_hash_func(req->set_link_operation_ptr, state);
+                break;
+        }
+        case REQUEST_TYPE_UP_DOWN:
+                break;
+        default:
+                assert_not_reached();
+        }
+}
+
+static int request_compare_func(const struct Request *a, const struct Request *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+        assert(a->link);
+        assert(b->link);
+
+        r = CMP(a->link->ifindex, b->link->ifindex);
+        if (r != 0)
+                return r;
+
+        r = CMP(a->type, b->type);
+        if (r != 0)
+                return r;
+
+        switch (a->type) {
+        case REQUEST_TYPE_ACTIVATE_LINK:
+                return 0;
+        case REQUEST_TYPE_ADDRESS:
+                return address_compare_func(a->address, b->address);
+        case REQUEST_TYPE_ADDRESS_LABEL:
+        case REQUEST_TYPE_BRIDGE_FDB:
+        case REQUEST_TYPE_BRIDGE_MDB:
+        case REQUEST_TYPE_STACKED_NETDEV:
+                return trivial_compare_func(a->object, b->object);
+        case REQUEST_TYPE_DHCP_SERVER:
+        case REQUEST_TYPE_DHCP4_CLIENT:
+        case REQUEST_TYPE_DHCP6_CLIENT:
+                return 0;
+        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp);
+        case REQUEST_TYPE_NEIGHBOR:
+                return neighbor_compare_func(a->neighbor, b->neighbor);
+        case REQUEST_TYPE_NEXTHOP:
+                return nexthop_compare_func(a->nexthop, b->nexthop);
+        case REQUEST_TYPE_ROUTE:
+                return route_compare_func(a->route, b->route);
+        case REQUEST_TYPE_RADV:
+                return 0;
+        case REQUEST_TYPE_ROUTING_POLICY_RULE:
+                return routing_policy_rule_compare_func(a->rule, b->rule);
+        case REQUEST_TYPE_SET_LINK:
+                return trivial_compare_func(a->set_link_operation_ptr, b->set_link_operation_ptr);
+        case REQUEST_TYPE_UP_DOWN:
+                return 0;
+        default:
+                assert_not_reached();
+        }
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                request_hash_ops,
+                Request,
+                request_hash_func,
+                request_compare_func,
+                request_free);
+
 int link_queue_request(
                 Link *link,
                 RequestType type,
@@ -64,13 +216,27 @@ int link_queue_request(
                 Request **ret) {
 
         _cleanup_(request_freep) Request *req = NULL;
+        Request *existing;
         int r;
 
         assert(link);
         assert(link->manager);
         assert(type >= 0 && type < _REQUEST_TYPE_MAX);
-        assert(object);
-        assert(netlink_handler);
+        assert(IN_SET(type,
+                      REQUEST_TYPE_ACTIVATE_LINK,
+                      REQUEST_TYPE_DHCP_SERVER,
+                      REQUEST_TYPE_DHCP4_CLIENT,
+                      REQUEST_TYPE_DHCP6_CLIENT,
+                      REQUEST_TYPE_RADV,
+                      REQUEST_TYPE_SET_LINK,
+                      REQUEST_TYPE_UP_DOWN) ||
+               object);
+        assert(IN_SET(type,
+                      REQUEST_TYPE_DHCP_SERVER,
+                      REQUEST_TYPE_DHCP4_CLIENT,
+                      REQUEST_TYPE_DHCP6_CLIENT,
+                      REQUEST_TYPE_RADV) ||
+               netlink_handler);
 
         req = new(Request, 1);
         if (!req) {
@@ -80,7 +246,7 @@ int link_queue_request(
         }
 
         *req = (Request) {
-                .link = link,
+                .link = link_ref(link),
                 .type = type,
                 .object = object,
                 .consume_object = consume_object,
@@ -88,9 +254,17 @@ int link_queue_request(
                 .netlink_handler = netlink_handler,
         };
 
-        link_ref(link);
+        existing = ordered_set_get(link->manager->request_queue, req);
+        if (existing) {
+                /* To prevent from removing the existing request. */
+                req->link = link_unref(req->link);
 
-        r = ordered_set_ensure_put(&link->manager->request_queue, NULL, req);
+                if (ret)
+                        *ret = existing;
+                return 0;
+        }
+
+        r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req);
         if (r < 0)
                 return r;
 
@@ -101,7 +275,7 @@ int link_queue_request(
                 *ret = req;
 
         TAKE_PTR(req);
-        return 0;
+        return 1;
 }
 
 int manager_process_requests(sd_event_source *s, void *userdata) {
@@ -116,21 +290,57 @@ int manager_process_requests(sd_event_source *s, void *userdata) {
 
                 ORDERED_SET_FOREACH(req, manager->request_queue) {
                         switch(req->type) {
+                        case REQUEST_TYPE_ACTIVATE_LINK:
+                                r = request_process_activation(req);
+                                break;
                         case REQUEST_TYPE_ADDRESS:
                                 r = request_process_address(req);
                                 break;
+                        case REQUEST_TYPE_ADDRESS_LABEL:
+                                r = request_process_address_label(req);
+                                break;
+                        case REQUEST_TYPE_BRIDGE_FDB:
+                                r = request_process_bridge_fdb(req);
+                                break;
+                        case REQUEST_TYPE_BRIDGE_MDB:
+                                r = request_process_bridge_mdb(req);
+                                break;
+                        case REQUEST_TYPE_DHCP_SERVER:
+                                r = request_process_dhcp_server(req);
+                                break;
+                        case REQUEST_TYPE_DHCP4_CLIENT:
+                                r = request_process_dhcp4_client(req);
+                                break;
+                        case REQUEST_TYPE_DHCP6_CLIENT:
+                                r = request_process_dhcp6_client(req);
+                                break;
+                        case REQUEST_TYPE_IPV6_PROXY_NDP:
+                                r = request_process_ipv6_proxy_ndp_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_RADV:
+                                r = request_process_radv(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;
+                        case REQUEST_TYPE_SET_LINK:
+                                r = request_process_set_link(req);
+                                break;
+                        case REQUEST_TYPE_STACKED_NETDEV:
+                                r = request_process_stacked_netdev(req);
+                                break;
+                        case REQUEST_TYPE_UP_DOWN:
+                                r = request_process_link_up_or_down(req);
+                                break;
                         default:
                                 return -EINVAL;
                         }