From 3f60e4488c5d52d01af07523cae6fa2f325014c9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 30 Jun 2021 01:11:07 +0900 Subject: [PATCH] sd-netlink: split sd_netlink_add_match() into two parts This also makes netlink_slot_disconnect() correctly unref multicast groups. --- src/libsystemd/sd-netlink/netlink-internal.h | 13 ++ src/libsystemd/sd-netlink/netlink-slot.c | 22 +-- src/libsystemd/sd-netlink/sd-netlink.c | 147 +++++++++++-------- 3 files changed, 99 insertions(+), 83 deletions(-) diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 2594885fe6..9025286add 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -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) diff --git a/src/libsystemd/sd-netlink/netlink-slot.c b/src/libsystemd/sd-netlink/netlink-slot.c index 76b4ccaa96..34f527d07f 100644 --- a/src/libsystemd/sd-netlink/netlink-slot.c +++ b/src/libsystemd/sd-netlink/netlink-slot.c @@ -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); + for (size_t i = 0; i < slot->match_callback.n_groups; i++) + (void) socket_broadcast_group_unref(nl, slot->match_callback.groups[i]); - 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; - } + slot->match_callback.n_groups = 0; + slot->match_callback.groups = mfree(slot->match_callback.groups); break; default: diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 2305333575..8ea3b2f1a0 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -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,75 +909,29 @@ 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); - r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot); + 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(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; - switch (type) { - case RTM_NEWLINK: - case RTM_DELLINK: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK); - if (r < 0) - return r; - - 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; - - break; - case RTM_NEWNEIGH: - case RTM_DELNEIGH: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH); - if (r < 0) - return r; - - 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; - 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; - break; - case RTM_NEWNEXTHOP: - case RTM_DELNEXTHOP: - r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP); - if (r < 0) - return r; - break; - - default: - return -EOPNOTSUPP; - } - - LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback); + 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; @@ -984,6 +940,67 @@ int sd_netlink_add_match( *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: + groups = link_groups; + n_groups = ELEMENTSOF(link_groups); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + groups = address_groups; + n_groups = ELEMENTSOF(address_groups); + break; + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + groups = neighbor_groups; + n_groups = ELEMENTSOF(neighbor_groups); + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + groups = route_groups; + n_groups = ELEMENTSOF(route_groups); + break; + case RTM_NEWRULE: + case RTM_DELRULE: + groups = rule_groups; + n_groups = ELEMENTSOF(rule_groups); + break; + case RTM_NEWNEXTHOP: + case RTM_DELNEXTHOP: + groups = nexthop_groups; + n_groups = ELEMENTSOF(nexthop_groups); + break; + default: + return -EOPNOTSUPP; + } + + return netlink_add_match_internal(rtnl, ret_slot, groups, n_groups, type, callback, + destroy_callback, userdata, description); +}