]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-netlink: split sd_netlink_add_match() into two parts
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 29 Jun 2021 16:11:07 +0000 (01:11 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 29 Aug 2021 09:10:47 +0000 (18:10 +0900)
This also makes netlink_slot_disconnect() correctly unref multicast
groups.

src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/netlink-slot.c
src/libsystemd/sd-netlink/sd-netlink.c

index 2594885fe65030c0285d1a187ba3e943148fa3d7..9025286addd0858f282e613de8651dabf4ff1e81 100644 (file)
@@ -25,6 +25,8 @@ struct reply_callback {
 
 struct match_callback {
         sd_netlink_message_handler_t callback;
+        uint32_t *groups;
+        size_t n_groups;
         uint16_t type;
 
         LIST_FIELDS(struct match_callback, match_callbacks);
@@ -151,6 +153,17 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m);
 int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount);
 int socket_read_message(sd_netlink *nl);
 
+int netlink_add_match_internal(
+                sd_netlink *nl,
+                sd_netlink_slot **ret_slot,
+                const uint32_t *groups,
+                size_t n_groups,
+                uint16_t type,
+                sd_netlink_message_handler_t callback,
+                sd_netlink_destroy_t destroy_callback,
+                void *userdata,
+                const char *description);
+
 /* Make sure callbacks don't destroy the netlink connection */
 #define NETLINK_DONT_DESTROY(nl) \
         _cleanup_(sd_netlink_unrefp) _unused_ sd_netlink *_dont_destroy_##nl = sd_netlink_ref(nl)
index 76b4ccaa960ae28f4cd50f2e4591a6c026997a32..34f527d07f5949fa7b65fe5af6f8a4b8859ea641 100644 (file)
@@ -70,25 +70,11 @@ void netlink_slot_disconnect(sd_netlink_slot *slot, bool unref) {
         case NETLINK_MATCH_CALLBACK:
                 LIST_REMOVE(match_callbacks, nl->match_callbacks, &slot->match_callback);
 
-                switch (slot->match_callback.type) {
-                case RTM_NEWLINK:
-                case RTM_DELLINK:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_LINK);
-
-                        break;
-                case RTM_NEWADDR:
-                case RTM_DELADDR:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_IFADDR);
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_IFADDR);
-
-                        break;
-                case RTM_NEWROUTE:
-                case RTM_DELROUTE:
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV4_ROUTE);
-                        (void) socket_broadcast_group_unref(nl, RTNLGRP_IPV6_ROUTE);
-
-                        break;
-                }
+                for (size_t i = 0; i < slot->match_callback.n_groups; i++)
+                        (void) socket_broadcast_group_unref(nl, slot->match_callback.groups[i]);
+
+                slot->match_callback.n_groups = 0;
+                slot->match_callback.groups = mfree(slot->match_callback.groups);
 
                 break;
         default:
index 2305333575f5c4533c3d8284c609506f85057369..8ea3b2f1a00534990467fa65e10741ddb0294870 100644 (file)
@@ -895,9 +895,11 @@ int sd_netlink_detach_event(sd_netlink *nl) {
         return 0;
 }
 
-int sd_netlink_add_match(
-                sd_netlink *rtnl,
+int netlink_add_match_internal(
+                sd_netlink *nl,
                 sd_netlink_slot **ret_slot,
+                const uint32_t *groups,
+                size_t n_groups,
                 uint16_t type,
                 sd_netlink_message_handler_t callback,
                 sd_netlink_destroy_t destroy_callback,
@@ -907,83 +909,98 @@ int sd_netlink_add_match(
         _cleanup_free_ sd_netlink_slot *slot = NULL;
         int r;
 
-        assert_return(rtnl, -EINVAL);
-        assert_return(callback, -EINVAL);
-        assert_return(!netlink_pid_changed(rtnl), -ECHILD);
+        assert(groups);
+        assert(n_groups > 0);
+
+        for (size_t i = 0; i < n_groups; i++) {
+                r = socket_broadcast_group_ref(nl, groups[i]);
+                if (r < 0)
+                        return r;
+        }
 
-        r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
+        r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
+                                  userdata, description, &slot);
         if (r < 0)
                 return r;
 
+        slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
+        if (!slot->match_callback.groups)
+                return -ENOMEM;
+
+        slot->match_callback.n_groups = n_groups;
         slot->match_callback.callback = callback;
         slot->match_callback.type = type;
 
+        LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
+
+        /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
+        slot->destroy_callback = destroy_callback;
+
+        if (ret_slot)
+                *ret_slot = slot;
+
+        TAKE_PTR(slot);
+        return 0;
+}
+
+int sd_netlink_add_match(
+                sd_netlink *rtnl,
+                sd_netlink_slot **ret_slot,
+                uint16_t type,
+                sd_netlink_message_handler_t callback,
+                sd_netlink_destroy_t destroy_callback,
+                void *userdata,
+                const char *description) {
+
+        static const uint32_t
+                address_groups[]  = { RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, },
+                link_groups[]     = { RTNLGRP_LINK, },
+                neighbor_groups[] = { RTNLGRP_NEIGH, },
+                nexthop_groups[]  = { RTNLGRP_NEXTHOP, },
+                route_groups[]    = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, },
+                rule_groups[]     = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, };
+        const uint32_t *groups;
+        size_t n_groups;
+
+        assert_return(rtnl, -EINVAL);
+        assert_return(callback, -EINVAL);
+        assert_return(!netlink_pid_changed(rtnl), -ECHILD);
+
         switch (type) {
                 case RTM_NEWLINK:
                 case RTM_DELLINK:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
-                        if (r < 0)
-                                return r;
-
+                        groups = link_groups;
+                        n_groups = ELEMENTSOF(link_groups);
                         break;
                 case RTM_NEWADDR:
                 case RTM_DELADDR:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
-                        if (r < 0)
-                                return r;
-
+                        groups = address_groups;
+                        n_groups = ELEMENTSOF(address_groups);
                         break;
                 case RTM_NEWNEIGH:
                 case RTM_DELNEIGH:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH);
-                        if (r < 0)
-                                return r;
-
+                        groups = neighbor_groups;
+                        n_groups = ELEMENTSOF(neighbor_groups);
                         break;
                 case RTM_NEWROUTE:
                 case RTM_DELROUTE:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
-                        if (r < 0)
-                                return r;
+                        groups = route_groups;
+                        n_groups = ELEMENTSOF(route_groups);
                         break;
                 case RTM_NEWRULE:
                 case RTM_DELRULE:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
-                        if (r < 0)
-                                return r;
-
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
-                        if (r < 0)
-                                return r;
+                        groups = rule_groups;
+                        n_groups = ELEMENTSOF(rule_groups);
                         break;
                 case RTM_NEWNEXTHOP:
                 case RTM_DELNEXTHOP:
-                        r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
-                        if (r < 0)
-                                return r;
-                break;
-
+                        groups = nexthop_groups;
+                        n_groups = ELEMENTSOF(nexthop_groups);
+                        break;
                 default:
                         return -EOPNOTSUPP;
         }
 
-        LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
-
-        /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
-        slot->destroy_callback = destroy_callback;
-
-        if (ret_slot)
-                *ret_slot = slot;
-
-        TAKE_PTR(slot);
-
-        return 0;
+        return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, callback,
+                                          destroy_callback, userdata, description);
 }