summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-06-30 01:11:07 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-08-29 18:10:47 +0900
commit3f60e4488c5d52d01af07523cae6fa2f325014c9 (patch)
treeb9fce20b3c0eedf2d817def7b76887e439fcc390
parentef90beb1c5c4041db8c33e28cea236567d40d1fd (diff)
downloadsystemd-3f60e4488c5d52d01af07523cae6fa2f325014c9.tar.gz
sd-netlink: split sd_netlink_add_match() into two parts
This also makes netlink_slot_disconnect() correctly unref multicast groups.
-rw-r--r--src/libsystemd/sd-netlink/netlink-internal.h13
-rw-r--r--src/libsystemd/sd-netlink/netlink-slot.c24
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c121
3 files changed, 87 insertions, 71 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);
-
- 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:
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,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);
}