diff --git a/src/core/platform/tests/test-platform-general.c b/src/core/platform/tests/test-platform-general.c index 908ad4dd3e..c18886c96e 100644 --- a/src/core/platform/tests/test-platform-general.c +++ b/src/core/platform/tests/test-platform-general.c @@ -788,6 +788,64 @@ test_route_type_is_nodev(void) /*****************************************************************************/ +static void +test_nmp_genl_family_type_from_name(void) +{ + int n_run; + int i; + + for (i = 0; i < (int) _NMP_GENL_FAMILY_TYPE_NUM; i++) { + const char *name = nmp_genl_family_infos[i].name; + + g_assert(name); + if (i > 0) + g_assert_cmpint(strcmp(nmp_genl_family_infos[i - 1].name, name), <, 0); + } + + g_assert_cmpint(nmp_genl_family_type_from_name("ethtool"), ==, NMP_GENL_FAMILY_TYPE_ETHTOOL); + g_assert_cmpint(nmp_genl_family_type_from_name("mptcp_pm"), ==, NMP_GENL_FAMILY_TYPE_MPTCP_PM); + g_assert_cmpint(nmp_genl_family_type_from_name("nl80211"), ==, NMP_GENL_FAMILY_TYPE_NL80211); + g_assert_cmpint(nmp_genl_family_type_from_name("nl802154"), ==, NMP_GENL_FAMILY_TYPE_NL802154); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguard"), + ==, + NMP_GENL_FAMILY_TYPE_WIREGUARD); + + g_assert_cmpint(nmp_genl_family_type_from_name(NULL), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("a"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguara"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("wireguarb"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name(""), ==, _NMP_GENL_FAMILY_TYPE_NONE); + g_assert_cmpint(nmp_genl_family_type_from_name("z"), ==, _NMP_GENL_FAMILY_TYPE_NONE); + + for (n_run = 0; n_run < 20; n_run++) { + for (i = 0; i < (int) _NMP_GENL_FAMILY_TYPE_NUM; i++) { + const char *cname = nmp_genl_family_infos[i].name; + const int ch_idx = nmtst_get_rand_uint() % strlen(cname); + char name[200]; + char ch; + gsize l; + + l = g_strlcpy(name, cname, sizeof(name)); + g_assert_cmpint(l, <, sizeof(name)); + + if (n_run == 0) { + g_assert_cmpint(nmp_genl_family_type_from_name(cname), ==, i); + g_assert_cmpint(nmp_genl_family_type_from_name(name), ==, i); + } + + /* randomly change one character in the name. Such a name becomes invalid. + * There are no two valid names which only differ by one characters. */ + do { + ch = nmtst_get_rand_uint() % 256; + } while (cname[ch_idx] == ch); + name[ch_idx] = ch; + g_assert_cmpint(nmp_genl_family_type_from_name(name), ==, _NMP_GENL_FAMILY_TYPE_NONE); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -808,6 +866,8 @@ main(int argc, char **argv) GINT_TO_POINTER(2), test_platform_ip_address_pretty_sort_cmp); g_test_add_func("/general/test_route_type_is_nodev", test_route_type_is_nodev); + g_test_add_func("/nm-platform/test_nmp_genl_family_type_from_name", + test_nmp_genl_family_type_from_name); return g_test_run(); } diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 6b39375136..185c9aed3e 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -79,6 +79,25 @@ enum { TCA_DEF_UNSPEC, TCA_DEF_TM, TCA_DEF_PARMS, TCA_DEF_DATA, TCA_DEF_PAD, __T /*****************************************************************************/ +typedef enum _nm_packed { + _NMP_NETLINK_FIRST = 0, + NMP_NETLINK_GENERIC = 0, + NMP_NETLINK_ROUTE, + _NMP_NETLINK_NUM, + _NMP_NETLINK_NONE = _NMP_NETLINK_NUM, +} NMPNetlinkProtocol; + +#define nmp_netlink_protocol_check(netlink_protocol) \ + ({ \ + const NMPNetlinkProtocol _netlink_protocol_2 = (netlink_protocol); \ + \ + nm_assert(NM_IN_SET(_netlink_protocol_2, NMP_NETLINK_ROUTE, NMP_NETLINK_GENERIC)); \ + \ + _netlink_protocol_2; \ + }) + +/*****************************************************************************/ + #ifndef IFLA_PROMISCUITY #define IFLA_PROMISCUITY 30 #endif @@ -279,6 +298,12 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ +typedef struct { + guint16 family_id; +} GenlFamilyData; + +/*****************************************************************************/ + typedef enum { INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD, @@ -302,15 +327,17 @@ typedef struct { typedef enum { _REFRESH_ALL_TYPE_FIRST = 0, - REFRESH_ALL_TYPE_LINKS = 0, - REFRESH_ALL_TYPE_IP4_ADDRESSES = 1, - REFRESH_ALL_TYPE_IP6_ADDRESSES = 2, - REFRESH_ALL_TYPE_IP4_ROUTES = 3, - REFRESH_ALL_TYPE_IP6_ROUTES = 4, - REFRESH_ALL_TYPE_ROUTING_RULES_IP4 = 5, - REFRESH_ALL_TYPE_ROUTING_RULES_IP6 = 6, - REFRESH_ALL_TYPE_QDISCS = 7, - REFRESH_ALL_TYPE_TFILTERS = 8, + REFRESH_ALL_TYPE_RTNL_LINKS = 0, + REFRESH_ALL_TYPE_RTNL_IP4_ADDRESSES = 1, + REFRESH_ALL_TYPE_RTNL_IP6_ADDRESSES = 2, + REFRESH_ALL_TYPE_RTNL_IP4_ROUTES = 3, + REFRESH_ALL_TYPE_RTNL_IP6_ROUTES = 4, + REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4 = 5, + REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6 = 6, + REFRESH_ALL_TYPE_RTNL_QDISCS = 7, + REFRESH_ALL_TYPE_RTNL_TFILTERS = 8, + + REFRESH_ALL_TYPE_GENL_FAMILIES = 9, _REFRESH_ALL_TYPE_NUM, } RefreshAllType; @@ -318,66 +345,79 @@ typedef enum { typedef struct { NMPObjectType obj_type; - /* for NLM_F_DUMP, which address family to request. */ - int addr_family; + NMPNetlinkProtocol protocol; + + /* For NLM_F_DUMP, which address family to request. + * Either AF_UNSPEC, AF_INET or AF_INET6. */ + gint8 addr_family_for_dump; } RefreshAllInfo; -typedef enum { +typedef enum _nm_packed { DELAYED_ACTION_TYPE_NONE = 0, #define F(val, name) ((sizeof(char[(((val)) == (name)) ? 1 : -1]) * 0) + (val)) - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS = 1 << F(0, REFRESH_ALL_TYPE_LINKS), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES = 1 << F(1, REFRESH_ALL_TYPE_IP4_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES = 1 << F(2, REFRESH_ALL_TYPE_IP6_ADDRESSES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES = 1 << F(3, REFRESH_ALL_TYPE_IP4_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES = 1 << F(4, REFRESH_ALL_TYPE_IP6_ROUTES), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 = 1 - << F(5, REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6 = 1 - << F(6, REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS = 1 << F(7, REFRESH_ALL_TYPE_QDISCS), - DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS = 1 << F(8, REFRESH_ALL_TYPE_TFILTERS), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_LINKS = 1 << F(0, REFRESH_ALL_TYPE_RTNL_LINKS), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES = + 1 << F(1, REFRESH_ALL_TYPE_RTNL_IP4_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES = + 1 << F(2, REFRESH_ALL_TYPE_RTNL_IP6_ADDRESSES), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES = 1 << F(3, REFRESH_ALL_TYPE_RTNL_IP4_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES = 1 << F(4, REFRESH_ALL_TYPE_RTNL_IP6_ROUTES), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP4 = + 1 << F(5, REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP6 = + 1 << F(6, REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS = 1 << F(7, REFRESH_ALL_TYPE_RTNL_QDISCS), + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS = 1 << F(8, REFRESH_ALL_TYPE_RTNL_TFILTERS), + + DELAYED_ACTION_TYPE_REFRESH_ALL_GENL_FAMILIES = 1 << F(9, REFRESH_ALL_TYPE_GENL_FAMILIES), #undef F - DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 9, - DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 10, - DELAYED_ACTION_TYPE_READ_NETLINK = 1 << 11, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = 1 << 12, + DELAYED_ACTION_TYPE_READ_RTNL = 1 << 10, + DELAYED_ACTION_TYPE_READ_GENL = 1 << 11, + DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL = 1 << 12, + DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL = 1 << 13, + DELAYED_ACTION_TYPE_REFRESH_LINK = 1 << 14, + DELAYED_ACTION_TYPE_MASTER_CONNECTED = 1 << 15, __DELAYED_ACTION_TYPE_MAX, - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL = - DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4 - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL = + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP4 + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP6, - DELAYED_ACTION_TYPE_REFRESH_ALL = - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, + DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_LINKS + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS, + + DELAYED_ACTION_TYPE_REFRESH_GENL_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_GENL_FAMILIES, DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX - 1, } DelayedActionType; -#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ - for ((iflags) = (DelayedActionType) 0x1LL; ({ \ - gboolean _good = FALSE; \ - \ - nm_assert(nm_utils_is_power_of_two(iflags)); \ - \ - while ((iflags) <= DELAYED_ACTION_TYPE_MAX) { \ - if (NM_FLAGS_ANY((flags_all), (iflags))) { \ - _good = TRUE; \ - break; \ - } \ - (iflags) <<= 1; \ - } \ - _good; \ - }); \ +#define FOR_EACH_DELAYED_ACTION(iflags, flags_all) \ + for ((iflags) = (DelayedActionType) 0x1LL; ({ \ + gboolean _good = FALSE; \ + \ + nm_assert((iflags) == 0 || nm_utils_is_power_of_two(iflags)); \ + \ + while ((iflags) != 0 && (iflags) <= DELAYED_ACTION_TYPE_MAX) { \ + if (NM_FLAGS_ANY((flags_all), (iflags))) { \ + _good = TRUE; \ + break; \ + } \ + (iflags) <<= 1; \ + } \ + _good; \ + }); \ (iflags) <<= 1) -typedef enum { +typedef enum _nm_packed { /* Negative values are errors from kernel. Add dummy member to * make enum signed. */ _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = G_MININT, @@ -392,40 +432,57 @@ typedef enum { WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS, } WaitForNlResponseResult; -typedef enum { +typedef enum _nm_packed { DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, } DelayedActionWaitForNlResponseType; typedef struct { - guint32 seq_number; - WaitForNlResponseResult seq_result; - DelayedActionWaitForNlResponseType response_type; - gint64 timeout_abs_nsec; - WaitForNlResponseResult *out_seq_result; - char **out_errmsg; + WaitForNlResponseResult *out_seq_result; + char **out_errmsg; union { int *out_refresh_all_in_progress; NMPObject **out_route_get; gpointer out_data; } response; + gint64 timeout_abs_nsec; + guint32 seq_number; + WaitForNlResponseResult seq_result; + DelayedActionWaitForNlResponseType response_type; } DelayedActionWaitForNlResponseData; /*****************************************************************************/ typedef struct { - struct nl_sock *sk_genl_sync; - - struct nl_sock *sk_rtnl; - - GSource *rtnl_event_source; - guint32 nlh_seq_next; + guint32 nlh_seq_last_seen; #if NM_MORE_LOGGING guint32 nlh_seq_last_handled; #endif - guint32 nlh_seq_last_seen; +} NetlinkProtocolPrivData; + +typedef struct { + struct nl_sock *sk_genl_sync; + + union { + struct { + struct nl_sock *sk_genl; + struct nl_sock *sk_rtnl; + }; + struct nl_sock *sk_x[_NMP_NETLINK_NUM]; + }; + + GSource *event_source_genl; + GSource *event_source_rtnl; + + union { + struct { + NetlinkProtocolPrivData proto_data_genl; + NetlinkProtocolPrivData proto_data_rtnl; + }; + NetlinkProtocolPrivData proto_data_x[_NMP_NETLINK_NUM]; + }; guint32 pruning[_REFRESH_ALL_TYPE_NUM]; @@ -446,7 +503,13 @@ typedef struct { GPtrArray *list_master_connected; GPtrArray *list_refresh_link; - GArray *list_wait_for_nl_response; + union { + struct { + GArray *list_wait_for_response_genl; + GArray *list_wait_for_response_rtnl; + }; + GArray *list_wait_for_response_x[_NMP_NETLINK_NUM]; + }; int is_handling; } delayed_action; @@ -462,6 +525,8 @@ typedef struct { gsize len; } netlink_recv_buf; + GenlFamilyData genl_family_data[_NMP_GENL_FAMILY_TYPE_NUM]; + } NMLinuxPlatformPrivate; struct _NMLinuxPlatform { @@ -562,7 +627,7 @@ NM_LINUX_PLATFORM_FROM_PRIVATE(NMLinuxPlatformPrivate *priv) static void delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpointer user_data); -static gboolean delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink); +static gboolean delayed_action_handle_all(NMPlatform *platform); static void do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name); static void do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type); static void cache_on_change(NMPlatform *platform, @@ -570,7 +635,36 @@ static void cache_on_change(NMPlatform *platform, const NMPObject *obj_old, const NMPObject *obj_new); static void cache_prune_all(NMPlatform *platform); -static gboolean event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks); +static gboolean event_handler_read_netlink(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + gboolean wait_for_acks); + +/*****************************************************************************/ + +static const struct { + gint8 netlink_protocol; + DelayedActionType delayed_action_type_read; + DelayedActionType delayed_action_type_wait_for_response; + const char name[5]; +} _nmp_netlink_protocol_infos[_NMP_NETLINK_NUM] = { + [NMP_NETLINK_ROUTE] = + { + .netlink_protocol = NETLINK_ROUTE, + .name = "rtnl", + .delayed_action_type_read = DELAYED_ACTION_TYPE_READ_RTNL, + .delayed_action_type_wait_for_response = DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL, + }, + [NMP_NETLINK_GENERIC] = + { + .netlink_protocol = NETLINK_GENERIC, + .name = "genl", + .delayed_action_type_read = DELAYED_ACTION_TYPE_READ_GENL, + .delayed_action_type_wait_for_response = DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL, + }, +}; + +#define nmp_netlink_protocol_info(netlink_protocol) \ + (&_nmp_netlink_protocol_infos[nmp_netlink_protocol_check((netlink_protocol))]) /*****************************************************************************/ @@ -2399,7 +2493,7 @@ _wireguard_get_device_cb(struct nl_msg *msg, void *arg) static const NMPObject * _wireguard_read_info(NMPlatform *platform /* used only as logging context */, struct nl_sock *genl, - int wireguard_family_id, + guint16 wireguard_family_id, int ifindex) { nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -2413,10 +2507,12 @@ _wireguard_read_info(NMPlatform *platform /* used only as logging context */ guint i; nm_assert(genl); - nm_assert(wireguard_family_id >= 0); nm_assert(ifindex > 0); - _LOGT("wireguard: fetching information for ifindex %d (genl-id %d)...", + if (wireguard_family_id == 0) + return NULL; + + _LOGT("wireguard: fetching information for ifindex %d (genl-id %u)...", ifindex, wireguard_family_id); @@ -2529,25 +2625,8 @@ nla_put_failure: g_return_val_if_reached(NULL); } -static int -_wireguard_get_family_id(NMPlatform *platform, int ifindex_try) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - int wireguard_family_id = -1; - - if (ifindex_try > 0) { - const NMPlatformLink *plink; - - if (nm_platform_link_get_lnk_wireguard(platform, ifindex_try, &plink)) - wireguard_family_id = NMP_OBJECT_UP_CAST(plink)->_link.wireguard_family_id; - } - if (wireguard_family_id < 0) - wireguard_family_id = genl_ctrl_resolve(priv->sk_genl_sync, "wireguard"); - return wireguard_family_id; -} - static const NMPObject * -_wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifindex) +_wireguard_refresh_link(NMPlatform *platform, guint16 wireguard_family_id, int ifindex) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -2557,7 +2636,7 @@ _wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifind const NMPObject *plink = NULL; nm_auto_nmpobj NMPObject *obj = NULL; - nm_assert(wireguard_family_id >= 0); + nm_assert(wireguard_family_id > 0); nm_assert(ifindex > 0); nm_platform_process_events(platform); @@ -2582,15 +2661,13 @@ _wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifind } } - if (plink->_link.wireguard_family_id == wireguard_family_id - && plink->_link.netlink.lnk == lnk_new) + if (plink->_link.netlink.lnk == lnk_new) return plink; /* we use nmp_cache_update_netlink() to re-inject the new object into the cache. * For that, we need to clone it, and tweak it so that it's suitable. It's a bit * of a hack, in particular that we need to clear driver and udev-device. */ - obj = nmp_object_clone(plink, FALSE); - obj->_link.wireguard_family_id = wireguard_family_id; + obj = nmp_object_clone(plink, FALSE); nmp_object_unref(obj->_link.netlink.lnk); obj->_link.netlink.lnk = g_steal_pointer(&lnk_new); obj->link.driver = NULL; @@ -2616,7 +2693,7 @@ _wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifind static int _wireguard_create_change_nlmsgs(NMPlatform *platform, int ifindex, - int wireguard_family_id, + guint16 wireguard_family_id, const NMPlatformLnkWireGuard *lnk_wireguard, const NMPWireGuardPeer *peers, const NMPlatformWireGuardChangePeerFlags *peer_flags, @@ -2858,12 +2935,12 @@ link_wireguard_change(NMPlatform *platform, { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); gs_unref_ptrarray GPtrArray *msgs = NULL; - int wireguard_family_id; + guint16 wireguard_family_id; guint i; int r; - wireguard_family_id = _wireguard_get_family_id(platform, ifindex); - if (wireguard_family_id < 0) + wireguard_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_WIREGUARD); + if (wireguard_family_id == 0) return -NME_PL_NO_FIRMWARE; r = _wireguard_create_change_nlmsgs(platform, @@ -3219,16 +3296,18 @@ _new_from_nl_link(NMPlatform *platform, switch (obj->link.type) { case NM_LINK_TYPE_WIFI: case NM_LINK_TYPE_OLPC_MESH: - obj->_link.ext_data = - (GObject *) nm_wifi_utils_new(ifi->ifi_index, - NM_LINUX_PLATFORM_GET_PRIVATE(platform)->sk_genl_sync, - TRUE); + obj->_link.ext_data = (GObject *) nm_wifi_utils_new( + NM_LINUX_PLATFORM_GET_PRIVATE(platform)->sk_genl_sync, + nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_NL80211), + ifi->ifi_index, + TRUE); break; case NM_LINK_TYPE_WPAN: - obj->_link.ext_data = - (GObject *) nm_wpan_utils_new(ifi->ifi_index, - NM_LINUX_PLATFORM_GET_PRIVATE(platform)->sk_genl_sync, - TRUE); + obj->_link.ext_data = (GObject *) nm_wpan_utils_new( + NM_LINUX_PLATFORM_GET_PRIVATE(platform)->sk_genl_sync, + nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_NL802154), + ifi->ifi_index, + TRUE); break; default: g_assert_not_reached(); @@ -3243,22 +3322,11 @@ _new_from_nl_link(NMPlatform *platform, * notifications, so we don't actually update the cache. For * now, always refetch link data here. */ - _lookup_cached_link(cache, obj->link.ifindex, completed_from_cache, &link_cached); - if (link_cached && link_cached->_link.netlink.is_in_netlink - && link_cached->link.type == NM_LINK_TYPE_WIREGUARD) - obj->_link.wireguard_family_id = link_cached->_link.wireguard_family_id; - else - obj->_link.wireguard_family_id = -1; - - if (obj->_link.wireguard_family_id < 0) - obj->_link.wireguard_family_id = genl_ctrl_resolve(genl, "wireguard"); - - if (obj->_link.wireguard_family_id >= 0) { - lnk_data_new = _wireguard_read_info(platform, - genl, - obj->_link.wireguard_family_id, - obj->link.ifindex); - } + lnk_data_new = _wireguard_read_info( + platform, + genl, + nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_WIREGUARD), + obj->link.ifindex); if (lnk_data_new && obj->_link.netlink.lnk && nmp_object_equal(obj->_link.netlink.lnk, lnk_data_new)) @@ -4664,8 +4732,8 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_link_full(int nlmsg_type, - int nlmsg_flags, +_nl_msg_new_link_full(uint16_t nlmsg_type, + uint16_t nlmsg_flags, int ifindex, const char *ifname, guint8 family, @@ -4697,15 +4765,15 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_link(int nlmsg_type, int nlmsg_flags, int ifindex, const char *ifname) +_nl_msg_new_link(uint16_t nlmsg_type, uint16_t nlmsg_flags, int ifindex, const char *ifname) { return _nl_msg_new_link_full(nlmsg_type, nlmsg_flags, ifindex, ifname, AF_UNSPEC, 0, 0); } /* Copied and modified from libnl3's build_addr_msg(). */ static struct nl_msg * -_nl_msg_new_address(int nlmsg_type, - int nlmsg_flags, +_nl_msg_new_address(uint16_t nlmsg_type, + uint16_t nlmsg_flags, int family, int ifindex, gconstpointer address, @@ -4819,7 +4887,7 @@ ip_route_ignored_protocol(const NMPlatformIPRoute *route) /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ static struct nl_msg * -_nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) +_nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *obj) { nm_auto_nlmsg struct nl_msg *msg = NULL; const NMPClass *klass = NMP_OBJECT_GET_CLASS(obj); @@ -4846,7 +4914,7 @@ _nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert(NM_IN_SET(nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - msg = nlmsg_alloc_simple(nlmsg_type, (int) nlmsgflags); + msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); if (nlmsg_append_struct(msg, &rtmsg) < 0) goto nla_put_failure; @@ -4930,7 +4998,9 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_routing_rule(int nlmsg_type, int nlmsg_flags, const NMPlatformRoutingRule *routing_rule) +_nl_msg_new_routing_rule(uint16_t nlmsg_type, + uint16_t nlmsg_flags, + const NMPlatformRoutingRule *routing_rule) { nm_auto_nlmsg struct nl_msg *msg = NULL; const guint8 addr_size = nm_utils_addr_family_to_size(routing_rule->addr_family); @@ -5043,7 +5113,7 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_qdisc(int nlmsg_type, int nlmsg_flags, const NMPlatformQdisc *qdisc) +_nl_msg_new_qdisc(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformQdisc *qdisc) { nm_auto_nlmsg struct nl_msg *msg = NULL; struct nlattr *tc_options; @@ -5130,7 +5200,7 @@ nla_put_failure: } static struct nl_msg * -_nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tfilter) +_nl_msg_new_tfilter(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPlatformTfilter *tfilter) { nm_auto_nlmsg struct nl_msg *msg = NULL; struct nlattr *tc_options; @@ -5788,7 +5858,10 @@ sysctl_get(NMPlatform *platform, const char *pathid, int dirfd, const char *path static void process_events(NMPlatform *platform) { - delayed_action_handle_all(platform, TRUE); + delayed_action_schedule(platform, + DELAYED_ACTION_TYPE_READ_RTNL | DELAYED_ACTION_TYPE_READ_GENL, + NULL); + delayed_action_handle_all(platform); } /*****************************************************************************/ @@ -5796,27 +5869,34 @@ process_events(NMPlatform *platform) static const RefreshAllInfo * refresh_all_type_get_info(RefreshAllType refresh_all_type) { - static const RefreshAllInfo infos[] = { -#define R(_refresh_all_type, _obj_type, _addr_family) \ - [_refresh_all_type] = { \ - .obj_type = _obj_type, \ - .addr_family = _addr_family, \ +#define R(_protocol, _refresh_all_type, _obj_type, _addr_family) \ + [_refresh_all_type] = { \ + .protocol = _protocol, \ + .obj_type = _obj_type, \ + .addr_family_for_dump = _addr_family, \ } - R(REFRESH_ALL_TYPE_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), - R(REFRESH_ALL_TYPE_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), - R(REFRESH_ALL_TYPE_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), - R(REFRESH_ALL_TYPE_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), - R(REFRESH_ALL_TYPE_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), - R(REFRESH_ALL_TYPE_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), -#undef R +#define R_ROUTE(...) R(NMP_NETLINK_ROUTE, __VA_ARGS__) +#define R_GENERIC(...) R(NMP_NETLINK_GENERIC, __VA_ARGS__) + static const RefreshAllInfo infos[] = { + R_ROUTE(REFRESH_ALL_TYPE_RTNL_LINKS, NMP_OBJECT_TYPE_LINK, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_IP4_ADDRESSES, NMP_OBJECT_TYPE_IP4_ADDRESS, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_IP4_ROUTES, NMP_OBJECT_TYPE_IP4_ROUTE, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_IP6_ROUTES, NMP_OBJECT_TYPE_IP6_ROUTE, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6, NMP_OBJECT_TYPE_ROUTING_RULE, AF_INET6), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_QDISCS, NMP_OBJECT_TYPE_QDISC, AF_UNSPEC), + R_ROUTE(REFRESH_ALL_TYPE_RTNL_TFILTERS, NMP_OBJECT_TYPE_TFILTER, AF_UNSPEC), + R_GENERIC(REFRESH_ALL_TYPE_GENL_FAMILIES, NMP_OBJECT_TYPE_UNKNOWN, AF_UNSPEC), }; +#undef R_GENERIC +#undef R_ROUTE +#undef R nm_assert(_NM_INT_NOT_NEGATIVE(refresh_all_type)); nm_assert(refresh_all_type < G_N_ELEMENTS(infos)); - nm_assert(nmp_class_from_type(infos[refresh_all_type].obj_type)); + nm_assert(refresh_all_type == REFRESH_ALL_TYPE_GENL_FAMILIES + || nmp_class_from_type(infos[refresh_all_type].obj_type)); return &infos[refresh_all_type]; } @@ -5826,19 +5906,24 @@ static NM_UTILS_LOOKUP_DEFINE( DelayedActionType, RefreshAllType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT(0), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, REFRESH_ALL_TYPE_LINKS), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, - REFRESH_ALL_TYPE_IP4_ADDRESSES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, - REFRESH_ALL_TYPE_IP6_ADDRESSES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, REFRESH_ALL_TYPE_IP4_ROUTES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, REFRESH_ALL_TYPE_IP6_ROUTES), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, - REFRESH_ALL_TYPE_ROUTING_RULES_IP4), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - REFRESH_ALL_TYPE_ROUTING_RULES_IP6), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, REFRESH_ALL_TYPE_QDISCS), - NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, REFRESH_ALL_TYPE_TFILTERS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_LINKS, REFRESH_ALL_TYPE_RTNL_LINKS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES, + REFRESH_ALL_TYPE_RTNL_IP4_ADDRESSES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES, + REFRESH_ALL_TYPE_RTNL_IP6_ADDRESSES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES, + REFRESH_ALL_TYPE_RTNL_IP4_ROUTES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES, + REFRESH_ALL_TYPE_RTNL_IP6_ROUTES), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP4, + REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP6, + REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS, REFRESH_ALL_TYPE_RTNL_QDISCS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS, + REFRESH_ALL_TYPE_RTNL_TFILTERS), + NM_UTILS_LOOKUP_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_GENL_FAMILIES, + REFRESH_ALL_TYPE_GENL_FAMILIES), NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER(), ); static DelayedActionType @@ -5860,25 +5945,25 @@ refresh_all_type_from_needle_object(const NMPObject *obj_needle) { switch (NMP_OBJECT_GET_TYPE(obj_needle)) { case NMP_OBJECT_TYPE_LINK: - return REFRESH_ALL_TYPE_LINKS; + return REFRESH_ALL_TYPE_RTNL_LINKS; case NMP_OBJECT_TYPE_IP4_ADDRESS: - return REFRESH_ALL_TYPE_IP4_ADDRESSES; + return REFRESH_ALL_TYPE_RTNL_IP4_ADDRESSES; case NMP_OBJECT_TYPE_IP6_ADDRESS: - return REFRESH_ALL_TYPE_IP6_ADDRESSES; + return REFRESH_ALL_TYPE_RTNL_IP6_ADDRESSES; case NMP_OBJECT_TYPE_IP4_ROUTE: - return REFRESH_ALL_TYPE_IP4_ROUTES; + return REFRESH_ALL_TYPE_RTNL_IP4_ROUTES; case NMP_OBJECT_TYPE_IP6_ROUTE: - return REFRESH_ALL_TYPE_IP6_ROUTES; + return REFRESH_ALL_TYPE_RTNL_IP6_ROUTES; case NMP_OBJECT_TYPE_QDISC: - return REFRESH_ALL_TYPE_QDISCS; + return REFRESH_ALL_TYPE_RTNL_QDISCS; case NMP_OBJECT_TYPE_TFILTER: - return REFRESH_ALL_TYPE_TFILTERS; + return REFRESH_ALL_TYPE_RTNL_TFILTERS; case NMP_OBJECT_TYPE_ROUTING_RULE: switch (NMP_OBJECT_CAST_ROUTING_RULE(obj_needle)->addr_family) { case AF_INET: - return REFRESH_ALL_TYPE_ROUTING_RULES_IP4; + return REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4; case AF_INET6: - return REFRESH_ALL_TYPE_ROUTING_RULES_IP6; + return REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6; } nm_assert_not_reached(); return 0; @@ -5902,11 +5987,11 @@ refresh_all_type_init_lookup(RefreshAllType refresh_all_type, NMPLookup *lookup) if (NM_IN_SET(refresh_all_info->obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)) { return nmp_lookup_init_object_by_addr_family(lookup, refresh_all_info->obj_type, - refresh_all_info->addr_family); + refresh_all_info->addr_family_for_dump); } /* not yet implemented. */ - nm_assert(refresh_all_info->addr_family == AF_UNSPEC); + nm_assert(refresh_all_info->addr_family_for_dump == AF_UNSPEC); return nmp_lookup_init_obj_type(lookup, refresh_all_info->obj_type); } @@ -5922,28 +6007,42 @@ static NM_UTILS_LOOKUP_STR_DEFINE( delayed_action_to_string, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT("unknown"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS, "refresh-all-links"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES, - "refresh-all-ip4-addresses"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, - "refresh-all-ip6-addresses"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES, "refresh-all-ip4-routes"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, "refresh-all-ip6-routes"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP4, - "refresh-all-routing-rules-ip4"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_IP6, - "refresh-all-routing-rules-ip6"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS, "refresh-all-qdiscs"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, "refresh-all-tfilters"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_LINKS, "refresh-all-rtnl-links"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES, + "refresh-all-rtnl-ip4-addresses"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES, + "refresh-all-rtnl-ip6-addresses"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES, + "refresh-all-rtnl-ip4-routes"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES, + "refresh-all-rtnl-ip6-routes"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP4, + "refresh-all-rtnl-routing-rules-ip4"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_IP6, + "refresh-all-rtnl-routing-rules-ip6"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS, + "refresh-all-rtnl-qdiscs"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS, + "refresh-all-rtnl-tfilters"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_ALL_GENL_FAMILIES, + "refresh-all-genl-families"), NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_REFRESH_LINK, "refresh-link"), NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_MASTER_CONNECTED, "master-connected"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_NETLINK, "read-netlink"), - NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, "wait-for-nl-response"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_RTNL, "read-rtnl"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_READ_GENL, "read-genl"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL, "wait-for-response-rtnl"), + NM_UTILS_LOOKUP_STR_ITEM(DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL, "wait-for-response-genl"), NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_NONE), - NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL), - NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL), + NM_UTILS_LOOKUP_ITEM_IGNORE(DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL), NM_UTILS_LOOKUP_ITEM_IGNORE(__DELAYED_ACTION_TYPE_MAX), ); +#define delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx) \ + (&g_array_index((priv)->delayed_action.list_wait_for_response_x[nmp_netlink_protocol_check( \ + (netlink_protocol))], \ + DelayedActionWaitForNlResponseData, \ + (idx))) + static const char * delayed_action_to_string_full(DelayedActionType action_type, gpointer user_data, @@ -5954,6 +6053,7 @@ delayed_action_to_string_full(DelayedActionType action_type, const DelayedActionWaitForNlResponseData *data; nm_strbuf_append_str(&buf, &buf_size, delayed_action_to_string(action_type)); + switch (action_type) { case DELAYED_ACTION_TYPE_MASTER_CONNECTED: nm_strbuf_append(&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT(user_data)); @@ -5961,7 +6061,8 @@ delayed_action_to_string_full(DelayedActionType action_type, case DELAYED_ACTION_TYPE_REFRESH_LINK: nm_strbuf_append(&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT(user_data)); break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: + case DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL: + case DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL: data = user_data; if (data) { @@ -6012,8 +6113,8 @@ delayed_action_refresh_all_in_progress(NMPlatform *platform, DelayedActionType a RefreshAllType refresh_all_type; nm_assert(nm_utils_is_power_of_two(action_type)); - nm_assert(NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_ALL)); - nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); + nm_assert(NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL)); + nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL)); if (NM_FLAGS_ANY(priv->delayed_action.flags, action_type)) return TRUE; @@ -6023,25 +6124,26 @@ delayed_action_refresh_all_in_progress(NMPlatform *platform, DelayedActionType a } static void -delayed_action_wait_for_nl_response_complete(NMPlatform *platform, - guint idx, - WaitForNlResponseResult seq_result) +delayed_action_wait_for_response_complete(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + guint idx, + WaitForNlResponseResult seq_result) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); DelayedActionWaitForNlResponseData *data; + const DelayedActionType ACTION_TYPE = + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response; - nm_assert(NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert(idx < priv->delayed_action.list_wait_for_nl_response->len); + nm_assert(NM_FLAGS_ANY(priv->delayed_action.flags, ACTION_TYPE)); + nm_assert(idx < priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len); nm_assert(seq_result); - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - idx); + data = delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, idx); - _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete"); + _LOGt_delayed_action(ACTION_TYPE, data, "complete"); - if (priv->delayed_action.list_wait_for_nl_response->len <= 1) - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; + if (priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len <= 1) + priv->delayed_action.flags &= ~ACTION_TYPE; if (data->out_seq_result) *data->out_seq_result = seq_result; switch (data->response_type) { @@ -6062,15 +6164,16 @@ delayed_action_wait_for_nl_response_complete(NMPlatform *platform, break; } - g_array_remove_index_fast(priv->delayed_action.list_wait_for_nl_response, idx); + g_array_remove_index_fast(priv->delayed_action.list_wait_for_response_x[netlink_protocol], idx); } static void -delayed_action_wait_for_nl_response_complete_check(NMPlatform *platform, - WaitForNlResponseResult force_result, - guint32 *out_next_seq_number, - gint64 *out_next_timeout_abs_ns, - gint64 *p_now_nsec) +delayed_action_wait_for_response_complete_check(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + WaitForNlResponseResult force_result, + guint32 *out_next_seq_number, + gint64 *out_next_timeout_abs_ns, + gint64 *p_now_nsec) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); guint i; @@ -6078,24 +6181,25 @@ delayed_action_wait_for_nl_response_complete_check(NMPlatform *platf gint64 next_timeout_abs_ns = 0; gint64 now_nsec = 0; - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len;) { + for (i = 0; i < priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len;) { const DelayedActionWaitForNlResponseData *data = - &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); + delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, i); if (data->seq_result) - delayed_action_wait_for_nl_response_complete(platform, i, data->seq_result); + delayed_action_wait_for_response_complete(platform, + netlink_protocol, + i, + data->seq_result); else if (p_now_nsec && ((now_nsec ?: (now_nsec = nm_utils_get_monotonic_timestamp_nsec())) >= data->timeout_abs_nsec)) { /* the caller can optionally check for timeout by providing a p_now_nsec argument. */ - delayed_action_wait_for_nl_response_complete( - platform, - i, - WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); + delayed_action_wait_for_response_complete(platform, + netlink_protocol, + i, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT); } else if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - delayed_action_wait_for_nl_response_complete(platform, i, force_result); + delayed_action_wait_for_response_complete(platform, netlink_protocol, i, force_result); else { if (next_seq_number == 0 || next_timeout_abs_ns > data->timeout_abs_nsec) { next_seq_number = data->seq_number; @@ -6106,9 +6210,10 @@ delayed_action_wait_for_nl_response_complete_check(NMPlatform *platf } if (force_result != WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) { - nm_assert( - !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); - nm_assert(priv->delayed_action.list_wait_for_nl_response->len == 0); + nm_assert(!NM_FLAGS_ANY( + priv->delayed_action.flags, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response)); + nm_assert(priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len == 0); } NM_SET_OUT(out_next_seq_number, next_seq_number); @@ -6118,9 +6223,15 @@ delayed_action_wait_for_nl_response_complete_check(NMPlatform *platf static void delayed_action_wait_for_nl_response_complete_all(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, WaitForNlResponseResult fallback_result) { - delayed_action_wait_for_nl_response_complete_check(platform, fallback_result, NULL, NULL, NULL); + delayed_action_wait_for_response_complete_check(platform, + nmp_netlink_protocol_check(netlink_protocol), + fallback_result, + NULL, + NULL, + NULL); } /*****************************************************************************/ @@ -6155,15 +6266,15 @@ delayed_action_handle_REFRESH_ALL(NMPlatform *platform, DelayedActionType flags) } static void -delayed_action_handle_READ_NETLINK(NMPlatform *platform) +delayed_action_handle_READ_NETLINK(NMPlatform *platform, NMPNetlinkProtocol netlink_protocol) { - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, netlink_protocol, FALSE); } static void -delayed_action_handle_WAIT_FOR_NL_RESPONSE(NMPlatform *platform) +delayed_action_handle_WAIT_FOR_RESPONSE(NMPlatform *platform, NMPNetlinkProtocol netlink_protocol) { - event_handler_read_netlink(platform, TRUE); + event_handler_read_netlink(platform, netlink_protocol, TRUE); } static gboolean @@ -6171,6 +6282,8 @@ delayed_action_handle_one(NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); gpointer user_data; + NMPNetlinkProtocol netlink_protocol; + DelayedActionType iflags; if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) return FALSE; @@ -6197,28 +6310,46 @@ delayed_action_handle_one(NMPlatform *platform) } nm_assert(priv->delayed_action.list_master_connected->len == 0); - /* Next we prefer read-netlink, because the buffer size is limited and we want to process events + /* Next we prefer read-genl/read-rtnl, because the buffer size is limited and we want to process events * from netlink early. */ - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_READ_NETLINK)) { - _LOGt_delayed_action(DELAYED_ACTION_TYPE_READ_NETLINK, NULL, "handle"); - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_READ_NETLINK; - delayed_action_handle_READ_NETLINK(platform); - return TRUE; + for (netlink_protocol = _NMP_NETLINK_FIRST; netlink_protocol < _NMP_NETLINK_NUM; + netlink_protocol++) { + const DelayedActionType ACTION_TYPE = + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_read; + + if (NM_FLAGS_ANY(priv->delayed_action.flags, ACTION_TYPE)) { + _LOGt_delayed_action(ACTION_TYPE, NULL, "handle"); + priv->delayed_action.flags &= ~ACTION_TYPE; + delayed_action_handle_READ_NETLINK(platform, netlink_protocol); + return TRUE; + } } - if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_ALL)) { - DelayedActionType flags, iflags; - - flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; - - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; + if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_GENL_ALL)) { + const DelayedActionType FLAGS = + priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_GENL_ALL; if (_LOGt_ENABLED()) { - FOR_EACH_DELAYED_ACTION (iflags, flags) + FOR_EACH_DELAYED_ACTION (iflags, FLAGS) _LOGt_delayed_action(iflags, NULL, "handle"); } - delayed_action_handle_REFRESH_ALL(platform, flags); + priv->delayed_action.flags &= ~FLAGS; + delayed_action_handle_REFRESH_ALL(platform, FLAGS); + return TRUE; + } + + if (NM_FLAGS_ANY(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL)) { + const DelayedActionType FLAGS = + (priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL); + + if (_LOGt_ENABLED()) { + FOR_EACH_DELAYED_ACTION (iflags, FLAGS) + _LOGt_delayed_action(iflags, NULL, "handle"); + } + + priv->delayed_action.flags &= ~FLAGS; + delayed_action_handle_REFRESH_ALL(platform, FLAGS); return TRUE; } @@ -6242,18 +6373,24 @@ delayed_action_handle_one(NMPlatform *platform) return TRUE; } - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - _LOGt_delayed_action(DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle"); - delayed_action_handle_WAIT_FOR_NL_RESPONSE(platform); - return TRUE; + for (netlink_protocol = _NMP_NETLINK_FIRST; netlink_protocol < _NMP_NETLINK_NUM; + netlink_protocol++) { + const DelayedActionType ACTION_TYPE = + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response; + + if (NM_FLAGS_ANY(priv->delayed_action.flags, ACTION_TYPE)) { + nm_assert(priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len > 0); + _LOGt_delayed_action(ACTION_TYPE, NULL, "handle"); + delayed_action_handle_WAIT_FOR_RESPONSE(platform, netlink_protocol); + return TRUE; + } } return FALSE; } static gboolean -delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink) +delayed_action_handle_all(NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); gboolean any = FALSE; @@ -6261,8 +6398,6 @@ delayed_action_handle_all(NMPlatform *platform, gboolean read_netlink) g_return_val_if_fail(priv->delayed_action.is_handling == 0, FALSE); priv->delayed_action.is_handling++; - if (read_netlink) - delayed_action_schedule(platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL); while (delayed_action_handle_one(platform)) any = TRUE; priv->delayed_action.is_handling--; @@ -6297,14 +6432,21 @@ delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpo < 0) g_ptr_array_add(priv->delayed_action.list_master_connected, user_data); break; - case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE: - g_array_append_vals(priv->delayed_action.list_wait_for_nl_response, user_data, 1); + case DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL: + g_array_append_vals(priv->delayed_action.list_wait_for_response_rtnl, user_data, 1); + break; + case DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL: + g_array_append_vals(priv->delayed_action.list_wait_for_response_genl, user_data, 1); break; default: + /* For other action types, we support setting multiple flags at once. They + * also don't have any user-data. */ nm_assert(!user_data); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)); - nm_assert(!NM_FLAGS_HAS(action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)); + nm_assert(!NM_FLAGS_ANY(action_type, + DELAYED_ACTION_TYPE_REFRESH_LINK + | DELAYED_ACTION_TYPE_MASTER_CONNECTED + | DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL + | DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_GENL)); break; } @@ -6317,12 +6459,37 @@ delayed_action_schedule(NMPlatform *platform, DelayedActionType action_type, gpo } static void -delayed_action_schedule_WAIT_FOR_NL_RESPONSE(NMPlatform *platform, - guint32 seq_number, - WaitForNlResponseResult *out_seq_result, - char **out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) +delayed_action_schedule_refresh_all(NMPlatform *platform, NMPNetlinkProtocol netlink_protocol) +{ + DelayedActionType action_type; + + if (netlink_protocol == NMP_NETLINK_ROUTE) { + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_LINKS + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL; + if (nm_platform_get_cache_tc(platform)) { + action_type |= (DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS); + } + } else { + nm_assert(netlink_protocol == NMP_NETLINK_GENERIC); + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL_GENL_FAMILIES; + } + + delayed_action_schedule(platform, action_type, NULL); +} + +static void +delayed_action_schedule_WAIT_FOR_RESPONSE(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + guint32 seq_number, + WaitForNlResponseResult *out_seq_result, + char **out_errmsg, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { DelayedActionWaitForNlResponseData data = { .seq_number = seq_number, @@ -6334,7 +6501,10 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE(NMPlatform * .response.out_data = response_out_data, }; - delayed_action_schedule(platform, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, &data); + delayed_action_schedule( + platform, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response, + &data); } /*****************************************************************************/ @@ -6386,6 +6556,9 @@ cache_prune_all(NMPlatform *platform) if (priv->pruning[refresh_all_type] == 0) continue; + + nm_assert(refresh_all_type != REFRESH_ALL_TYPE_GENL_FAMILIES); + priv->pruning[refresh_all_type] -= 1; if (priv->pruning[refresh_all_type] > 0) continue; @@ -6469,15 +6642,18 @@ cache_on_change(NMPlatform *platform, ifindex = obj_new->link.ifindex; if (ifindex > 0) { - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); + delayed_action_schedule( + platform, + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ADDRESSES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL + | (nm_platform_get_cache_tc(platform) + ? (DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_QDISCS + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_TFILTERS) + : DELAYED_ACTION_TYPE_NONE), + NULL); } } { @@ -6525,8 +6701,8 @@ cache_on_change(NMPlatform *platform, * think kernel does send RTM_DELROUTE events for IPv6 routes, so * we might not need to refresh IPv6 routes. */ delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES + | DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES, NULL); } } @@ -6634,8 +6810,8 @@ cache_on_change(NMPlatform *platform, if (cache_op == NMP_CACHE_OPS_REMOVED) { delayed_action_schedule(platform, (klass->obj_type == NMP_OBJECT_TYPE_IP4_ADDRESS) - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES, + ? DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP4_ROUTES + : DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_IP6_ROUTES, NULL); } } break; @@ -6647,12 +6823,14 @@ cache_on_change(NMPlatform *platform, /*****************************************************************************/ static guint32 -_nlh_seq_next_get(NMLinuxPlatformPrivate *priv) +_nlh_seq_next_get(NMLinuxPlatformPrivate *priv, NMPNetlinkProtocol netlink_protocol) { + guint32 *p = &priv->proto_data_x[netlink_protocol].nlh_seq_next; + /* generate a new sequence number, but never return zero. * Wrapping numbers are not a problem, because we don't rely * on strictly increasing sequence numbers. */ - return (++priv->nlh_seq_next) ?: (++priv->nlh_seq_next); + return (++(*p)) ?: (++(*p)); } /** @@ -6679,7 +6857,7 @@ _nl_send_nlmsghdr(NMPlatform *platform, nm_assert(nlhdr); - seq = _nlh_seq_next_get(priv); + seq = _nlh_seq_next_get(priv, NMP_NETLINK_ROUTE); nlhdr->nlmsg_seq = seq; { @@ -6713,32 +6891,24 @@ again: } } - delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, - seq, - out_seq_result, - out_errmsg, - response_type, - response_out_data); + delayed_action_schedule_WAIT_FOR_RESPONSE(platform, + NMP_NETLINK_ROUTE, + seq, + out_seq_result, + out_errmsg, + response_type, + response_out_data); return 0; } -/** - * _nl_send_nlmsg: - * @platform: - * @nlmsg: - * @out_seq_result: - * @response_type: - * @response_out_data: - * - * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). - */ static int -_nl_send_nlmsg(NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - char **out_errmsg, - DelayedActionWaitForNlResponseType response_type, - gpointer response_out_data) +_netlink_send_nlmsg(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + char **out_errmsg, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); struct nlmsghdr *nlhdr; @@ -6746,24 +6916,40 @@ _nl_send_nlmsg(NMPlatform *platform, int nle; nlhdr = nlmsg_hdr(nlmsg); - seq = _nlh_seq_next_get(priv); + seq = _nlh_seq_next_get(priv, netlink_protocol); nlhdr->nlmsg_seq = seq; - nle = nl_send_auto(priv->sk_rtnl, nlmsg); + nle = nl_send_auto(priv->sk_x[netlink_protocol], nlmsg); if (nle < 0) { _LOGD("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nm_strerror(nle), nle); return nle; } - delayed_action_schedule_WAIT_FOR_NL_RESPONSE(platform, - seq, - out_seq_result, - out_errmsg, - response_type, - response_out_data); + delayed_action_schedule_WAIT_FOR_RESPONSE(platform, + netlink_protocol, + seq, + out_seq_result, + out_errmsg, + response_type, + response_out_data); return 0; } +static int +_netlink_send_nlmsg_rtnl(NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + char **out_errmsg) +{ + return _netlink_send_nlmsg(platform, + NMP_NETLINK_ROUTE, + nlmsg, + out_seq_result, + out_errmsg, + DELAYED_ACTION_RESPONSE_TYPE_VOID, + NULL); +} + static void do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char *name) { @@ -6783,16 +6969,16 @@ do_request_link_no_delayed_actions(NMPlatform *platform, int ifindex, const char entry = nmp_cache_lookup_entry_link(nm_platform_get_cache(platform), ifindex); if (entry) { - priv->pruning[REFRESH_ALL_TYPE_LINKS] += 1; + priv->pruning[REFRESH_ALL_TYPE_RTNL_LINKS] += 1; nm_dedup_multi_entry_set_dirty(entry, TRUE); } } - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); nlmsg = _nl_msg_new_link(RTM_GETLINK, 0, ifindex, name); if (nlmsg) { - nle = _nl_send_nlmsg(platform, nlmsg, NULL, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + nle = _netlink_send_nlmsg_rtnl(platform, nlmsg, NULL, NULL); if (nle < 0) { _LOGE("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", ifindex, @@ -6808,11 +6994,11 @@ static void do_request_link(NMPlatform *platform, int ifindex, const char *name) { do_request_link_no_delayed_actions(platform, ifindex, name); - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); } static struct nl_msg * -_nl_msg_new_dump(NMPObjectType obj_type, int preferred_addr_family) +_nl_msg_new_dump_rtnl(NMPObjectType obj_type, int preferred_addr_family) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const NMPClass *klass; @@ -6862,6 +7048,26 @@ _nl_msg_new_dump(NMPObjectType obj_type, int preferred_addr_family) return g_steal_pointer(&nlmsg); } +static struct nl_msg * +_nl_msg_new_dump_genl_families(void) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN)); + + if (!genlmsg_put(nlmsg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + GENL_ID_CTRL, + 0, + NLM_F_DUMP, + CTRL_CMD_GETFAMILY, + 1)) + g_return_val_if_reached(NULL); + + return g_steal_pointer(&nlmsg); +} + static void do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action_type) { @@ -6869,29 +7075,36 @@ do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action DelayedActionType action_type_prune; DelayedActionType iflags; - nm_assert(!NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_ALL)); - action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; + nm_assert((NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL) + && !NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_RTNL_ALL)) + || (NM_FLAGS_ANY(action_type, DELAYED_ACTION_TYPE_REFRESH_GENL_ALL) + && !NM_FLAGS_ANY(action_type, ~DELAYED_ACTION_TYPE_REFRESH_GENL_ALL))); action_type_prune = action_type; - /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra - * cache lookup for every entry. - * - * Avoid that, by special casing routing-rules here. */ - if (NM_FLAGS_ALL(action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL)) { + if (NM_FLAGS_ALL(action_type_prune, DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL)) { NMPLookup lookup; - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP4] += 1; - priv->pruning[REFRESH_ALL_TYPE_ROUTING_RULES_IP6] += 1; + /* calling nmp_cache_dirty_set_all_main() with a non-main lookup-index requires an extra + * cache lookup for every entry. + * + * Avoid that, by special casing routing-rules here. */ + priv->pruning[REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP4] += 1; + priv->pruning[REFRESH_ALL_TYPE_RTNL_ROUTING_RULES_IP6] += 1; nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); - action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL; + action_type_prune &= ~DELAYED_ACTION_TYPE_REFRESH_ALL_RTNL_ROUTING_RULES_ALL; } - FOR_EACH_DELAYED_ACTION (iflags, action_type_prune) { RefreshAllType refresh_all_type = delayed_action_type_to_refresh_all_type(iflags); NMPLookup lookup; + if (refresh_all_type == REFRESH_ALL_TYPE_GENL_FAMILIES) { + /* genl families are not tracked in the NMPObject cache, because there is + * only a static number of objects we care about (NMPGenlFamilyType). */ + continue; + } + priv->pruning[refresh_all_type] += 1; refresh_all_type_init_lookup(refresh_all_type, &lookup); nmp_cache_dirty_set_all_main(nm_platform_get_cache(platform), &lookup); @@ -6912,7 +7125,7 @@ do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action priv->delayed_action.flags &= ~iflags; _LOGt_delayed_action(iflags, NULL, "handle (do-request-all)"); - if (refresh_all_type == REFRESH_ALL_TYPE_LINKS) { + if (refresh_all_type == REFRESH_ALL_TYPE_RTNL_LINKS) { nm_assert( (priv->delayed_action.list_refresh_link->len > 0) == NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)); @@ -6925,23 +7138,30 @@ do_request_all_no_delayed_actions(NMPlatform *platform, DelayedActionType action } } - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, refresh_all_info->protocol, FALSE); + + if (refresh_all_info->protocol == NMP_NETLINK_ROUTE) { + nlmsg = _nl_msg_new_dump_rtnl(refresh_all_info->obj_type, + refresh_all_info->addr_family_for_dump); + } else { + nm_assert(refresh_all_type == REFRESH_ALL_TYPE_GENL_FAMILIES); + nlmsg = _nl_msg_new_dump_genl_families(); + } - nlmsg = _nl_msg_new_dump(refresh_all_info->obj_type, refresh_all_info->addr_family); if (!nlmsg) goto next_after_fail; - if (_nl_send_nlmsg(platform, - nlmsg, - NULL, - NULL, - DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, - out_refresh_all_in_progress) + if (_netlink_send_nlmsg(platform, + refresh_all_info->protocol, + nlmsg, + NULL, + NULL, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, + out_refresh_all_in_progress) < 0) goto next_after_fail; continue; - next_after_fail: nm_assert(*out_refresh_all_in_progress > 0); *out_refresh_all_in_progress -= 1; @@ -6953,86 +7173,94 @@ do_request_one_type_by_needle_object(NMPlatform *platform, const NMPObject *obj_ { do_request_all_no_delayed_actions(platform, delayed_action_refresh_from_needle_object(obj_needle)); - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); } static void -event_seq_check_refresh_all(NMPlatform *platform, guint32 seq_number) +event_seq_check_refresh_all(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + guint32 seq_number) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionWaitForNlResponseData *data; - guint i; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + guint i; - if (NM_IN_SET(seq_number, 0, priv->nlh_seq_last_seen)) + if (NM_IN_SET(seq_number, 0, priv->proto_data_x[netlink_protocol].nlh_seq_last_seen)) return; - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); + if (!NM_FLAGS_ANY( + priv->delayed_action.flags, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response)) + goto out; - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); + nm_assert(priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len > 0); - if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS - && data->response.out_refresh_all_in_progress - && data->seq_number == priv->nlh_seq_last_seen) { - *data->response.out_refresh_all_in_progress -= 1; - data->response.out_refresh_all_in_progress = NULL; - break; - } + for (i = 0; i < priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len; i++) { + DelayedActionWaitForNlResponseData *data = + delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, i); + + if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progress + && data->seq_number == priv->proto_data_x[netlink_protocol].nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progress -= 1; + data->response.out_refresh_all_in_progress = NULL; + break; } } - priv->nlh_seq_last_seen = seq_number; +out: + priv->proto_data_x[netlink_protocol].nlh_seq_last_seen = seq_number; } static void event_seq_check(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, guint32 seq_number, WaitForNlResponseResult seq_result, const char *msg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - DelayedActionWaitForNlResponseData *data; - guint i; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + guint i; if (seq_number == 0) return; - if (NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); + if (!NM_FLAGS_ANY( + priv->delayed_action.flags, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response)) + goto out; - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { - data = &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); + nm_assert(priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len > 0); - if (data->seq_number == seq_number) { - /* We potentially receive many parts partial responses for the same sequence number. + for (i = 0; i < priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len; i++) { + DelayedActionWaitForNlResponseData *data = + delayed_action_get_list_wait_for_resonse(priv, netlink_protocol, i); + + if (data->seq_number == seq_number) { + /* We potentially receive many parts partial responses for the same sequence number. * Thus, we only remember the result, and collect it later. */ - if (data->seq_result < 0) { - /* we already saw an error for this sequence number. + if (data->seq_result < 0) { + /* we already saw an error for this sequence number. * Preserve it. */ - } else if (seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN - || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) - data->seq_result = seq_result; - if (data->out_errmsg && !*data->out_errmsg) - *data->out_errmsg = g_strdup(msg); - return; - } + } else if (seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN + || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN) + data->seq_result = seq_result; + if (data->out_errmsg && !*data->out_errmsg) + *data->out_errmsg = g_strdup(msg); + return; } } +out: + #if NM_MORE_LOGGING - if (seq_number != priv->nlh_seq_last_handled) + if (seq_number != priv->proto_data_x[netlink_protocol].nlh_seq_last_handled) _LOGt("netlink: recvmsg: unwaited sequence number %u", seq_number); - priv->nlh_seq_last_handled = seq_number; + priv->proto_data_x[netlink_protocol].nlh_seq_last_handled = seq_number; #endif } static void -_rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg, gboolean handle_events) +_rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg) { char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMLinuxPlatformPrivate *priv; @@ -7045,9 +7273,6 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg, gboolean h NMPCache *cache = nm_platform_get_cache(platform); ParseNlmsgIter parse_nlmsg_iter; - if (!handle_events) - return; - msghdr = msg->nm_nlh; if (NM_IN_SET(msghdr->nlmsg_type, @@ -7126,15 +7351,13 @@ _rtnl_handle_msg(NMPlatform *platform, const struct nl_msg_lite *msg, gboolean h nm_assert(is_ipv6 || !nmp_object_is_alive(obj)); priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); if (NM_FLAGS_HAS(priv->delayed_action.flags, - DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + DELAYED_ACTION_TYPE_WAIT_FOR_RESPONSE_RTNL)) { guint i; - nm_assert(priv->delayed_action.list_wait_for_nl_response->len > 0); - for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + nm_assert(priv->delayed_action.list_wait_for_response_rtnl->len > 0); + for (i = 0; i < priv->delayed_action.list_wait_for_response_rtnl->len; i++) { DelayedActionWaitForNlResponseData *data = - &g_array_index(priv->delayed_action.list_wait_for_nl_response, - DelayedActionWaitForNlResponseData, - i); + delayed_action_get_list_wait_for_resonse(priv, NMP_NETLINK_ROUTE, i); if (data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET && data->response.out_route_get) { @@ -7276,14 +7499,9 @@ do_add_link_with_lookup(NMPlatform *platform, char s_buf[256]; NMPCache *cache = nm_platform_get_cache(platform); - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, nlmsg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, @@ -7294,7 +7512,7 @@ do_add_link_with_lookup(NMPlatform *platform, return nle; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -7330,14 +7548,9 @@ do_add_addrroute(NMPlatform *platform, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, nlmsg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, @@ -7347,7 +7560,7 @@ do_add_addrroute(NMPlatform *platform, return -NME_PL_NETLINK; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -7386,14 +7599,9 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n gboolean success; const char *log_detail = ""; - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, nlmsg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS(obj_id)->obj_type_name, @@ -7403,7 +7611,7 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n return FALSE; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -7477,12 +7685,7 @@ do_change_link(NMPlatform *platform, } retry: - nle = _nl_send_nlmsg(platform, - nlmsg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, nlmsg, &seq_result, &errmsg); if (nle < 0) { log_level = LOGL_ERR; log_detail_free = @@ -7495,7 +7698,7 @@ retry: * and we sometimes lack events. Nuke it from the orbit... */ delayed_action_schedule(platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER(ifindex)); - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -8974,7 +9177,7 @@ ip_route_get(NMPlatform *platform, return -NME_UNSPEC; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); /* Retry, if we failed due to a cache resync. That can happen when the netlink * socket fills up and we lost the response. */ @@ -9010,20 +9213,15 @@ routing_rule_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformRoutin msg = _nl_msg_new_routing_rule(RTM_NEWRULE, flags, routing_rule); - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, msg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-add-rule: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); return -NME_PL_NETLINK; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -9054,20 +9252,15 @@ qdisc_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) msg = _nl_msg_new_qdisc(RTM_NEWQDISC, flags, qdisc); - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, msg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-add-qdisc: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); return -NME_PL_NETLINK; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -9083,7 +9276,11 @@ qdisc_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) } static int -tc_delete(NMPlatform *platform, int nlmsgtype, int ifindex, guint32 parent, gboolean log_error) +tc_delete(NMPlatform *platform, + uint16_t nlmsg_type, + int ifindex, + guint32 parent, + gboolean log_error) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; @@ -9096,7 +9293,7 @@ tc_delete(NMPlatform *platform, int nlmsgtype, int ifindex, guint32 parent, gboo .tcm_parent = parent, }; - switch (nlmsgtype) { + switch (nlmsg_type) { case RTM_DELQDISC: log_tag = "do-delete-qdisc"; break; @@ -9108,19 +9305,14 @@ tc_delete(NMPlatform *platform, int nlmsgtype, int ifindex, guint32 parent, gboo log_tag = "do-delete-tc"; } - msg = nlmsg_alloc_simple(nlmsgtype, NMP_NLM_FLAG_F_ECHO); + msg = nlmsg_alloc_simple(nlmsg_type, NMP_NLM_FLAG_F_ECHO); if (nlmsg_append_struct(msg, &tcm) < 0) goto nla_put_failure; - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, msg, &seq_result, &errmsg); if (nle < 0) { _NMLOG(log_error ? LOGL_ERR : LOGL_DEBUG, "%s: failed sending netlink request \"%s\" (%d)", @@ -9130,7 +9322,7 @@ tc_delete(NMPlatform *platform, int nlmsgtype, int ifindex, guint32 parent, gboo return -NME_PL_NETLINK; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -9170,20 +9362,15 @@ tfilter_add(NMPlatform *platform, NMPNlmFlags flags, const NMPlatformTfilter *tf msg = _nl_msg_new_tfilter(RTM_NEWTFILTER, flags, tfilter); - event_handler_read_netlink(platform, FALSE); + event_handler_read_netlink(platform, NMP_NETLINK_ROUTE, FALSE); - nle = _nl_send_nlmsg(platform, - msg, - &seq_result, - &errmsg, - DELAYED_ACTION_RESPONSE_TYPE_VOID, - NULL); + nle = _netlink_send_nlmsg_rtnl(platform, msg, &seq_result, &errmsg); if (nle < 0) { _LOGE("do-add-tfilter: failed sending netlink request \"%s\" (%d)", nm_strerror(nle), -nle); return -NME_PL_NETLINK; } - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); nm_assert(seq_result); @@ -9205,12 +9392,66 @@ tfilter_delete(NMPlatform *platform, int ifindex, guint32 parent, gboolean log_e /*****************************************************************************/ +static void +_genl_handle_msg_ctrl(NMPlatform *platform, const struct nlmsghdr *hdr) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + const struct genlmsghdr *ghdr = nlmsg_data(hdr); + + switch (ghdr->cmd) { + case CTRL_CMD_NEWFAMILY: + case CTRL_CMD_DELFAMILY: + { + struct nlattr *tb[G_N_ELEMENTS(genl_ctrl_policy)]; + NMPGenlFamilyType family_type; + const char *name; + guint16 family_id = 0; + + if (genlmsg_parse_arr(hdr, 0, tb, genl_ctrl_policy) < 0) + return; + + name = nla_get_string(tb[CTRL_ATTR_FAMILY_NAME]); + family_type = nmp_genl_family_type_from_name(name); + + if (family_type == _NMP_GENL_FAMILY_TYPE_NONE) + return; + + if (ghdr->cmd == CTRL_CMD_NEWFAMILY) + family_id = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + + if (priv->genl_family_data[family_type].family_id != family_id) { + if (family_id != 0) + _LOGD("genl:ctrl: new family-id for %s: 0x%04x", name, family_id); + else + _LOGD("genl:ctrl: del family-id for %s", name); + priv->genl_family_data[family_type].family_id = family_id; + } + } + } +} + +static void +_genl_handle_msg(NMPlatform *platform, guint32 pktinfo_group, const struct nl_msg_lite *msg) +{ + const struct nlmsghdr *hdr = msg->nm_nlh; + + if (!genlmsg_valid_hdr(hdr, 0)) + return; + + if (hdr->nlmsg_type == GENL_ID_CTRL) + _genl_handle_msg_ctrl(platform, hdr); +} + +/*****************************************************************************/ + static int _netlink_recv(NMPlatform *platform, struct nl_sock *sk, struct sockaddr_nl *nla, struct ucred *out_creds, - gboolean *out_creds_has) + gboolean *out_creds_has, + guint32 *out_pktinfo_group, + gboolean *out_pktinfo_has) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); unsigned char *buf = NULL; @@ -9220,7 +9461,10 @@ _netlink_recv(NMPlatform *platform, nm_assert(out_creds); nm_assert(out_creds_has); - /* We use a pre-allocated receive buffer. */ + /* We use a pre-allocated receive buffer. We use it both for sk_rtnl + * and sk_genl. We can do that, because we are deep inside the netlink + * handling, and we never will need to use it for both sockets at the + * same time. */ n = nl_recv(sk, priv->netlink_recv_buf.buf, @@ -9229,8 +9473,8 @@ _netlink_recv(NMPlatform *platform, &buf, out_creds, out_creds_has, - NULL, - NULL); + out_pktinfo_group, + out_pktinfo_has); nm_assert((n <= 0 && !buf) || (n > 0 && n <= priv->netlink_recv_buf.len && buf == priv->netlink_recv_buf.buf)); @@ -9251,19 +9495,33 @@ _netlink_recv(NMPlatform *platform, /*****************************************************************************/ static gboolean -rtnl_event_handler(int fd, GIOCondition io_condition, gpointer user_data) +_nl_event_handler(NMPlatform *platform, DelayedActionType action_type) { - delayed_action_handle_all(NM_PLATFORM(user_data), TRUE); + delayed_action_schedule(platform, action_type, NULL); + delayed_action_handle_all(platform); return TRUE; } +static gboolean +_nl_event_handler_genl(int fd, GIOCondition io_condition, gpointer user_data) +{ + return _nl_event_handler(user_data, DELAYED_ACTION_TYPE_READ_GENL); +} + +static gboolean +_nl_event_handler_rtnl(int fd, GIOCondition io_condition, gpointer user_data) +{ + return _nl_event_handler(user_data, DELAYED_ACTION_TYPE_READ_RTNL); +} + /*****************************************************************************/ static int -_netlink_recv_handle(NMPlatform *platform, int netlink_protocol, gboolean handle_events) +_netlink_recv_handle(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + gboolean handle_events) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - struct nl_sock *sk; int n; int retval = 0; gboolean multipart = 0; @@ -9272,15 +9530,19 @@ _netlink_recv_handle(NMPlatform *platform, int netlink_protocol, gboolean handle struct sockaddr_nl nla; struct ucred creds; gboolean creds_has; - const char *log_prefix; - - nm_assert(netlink_protocol == NETLINK_ROUTE); - sk = priv->sk_rtnl; - log_prefix = "rtnl"; + guint32 pktinfo_group = 0; + gboolean pktinfo_has = FALSE; + const char *const log_prefix = nmp_netlink_protocol_info(netlink_protocol)->name; continue_reading: - n = _netlink_recv(platform, sk, &nla, &creds, &creds_has); + n = _netlink_recv(platform, + priv->sk_x[netlink_protocol], + &nla, + &creds, + &creds_has, + &pktinfo_group, + netlink_protocol == NMP_NETLINK_GENERIC ? &pktinfo_has : NULL); if (n < 0) { if (n == -NME_NL_MSG_TRUNC && !handle_events) goto continue_reading; @@ -9302,7 +9564,7 @@ continue_reading: char buf_nlmsghdr[400]; const char *extack_msg = NULL; const struct nl_msg_lite msg = { - .nm_protocol = netlink_protocol, + .nm_protocol = nmp_netlink_protocol_info(netlink_protocol)->netlink_protocol, .nm_src = &nla, .nm_creds = &creds, .nm_size = NLMSG_ALIGN(hdr->nlmsg_len), @@ -9314,8 +9576,8 @@ continue_reading: _LOGt("%s: recvmsg: new message %s", log_prefix, - nl_nlmsghdr_to_str(netlink_protocol, - 0, + nl_nlmsghdr_to_str(nmp_netlink_protocol_info(netlink_protocol)->netlink_protocol, + pktinfo_group, msg.nm_nlh, buf_nlmsghdr, sizeof(buf_nlmsghdr))); @@ -9332,9 +9594,8 @@ continue_reading: interrupted = TRUE; } - /* Other side wishes to see an ack for this message */ if (msg.nm_nlh->nlmsg_flags & NLM_F_ACK) { - /* FIXME: implement */ + /* TODO: Other side wishes to see an ack for this message */ } seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; @@ -9391,7 +9652,8 @@ continue_reading: } /* Error message reported back from kernel. */ - _LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", + _LOGD("%s: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", + log_prefix, nm_strerror_native(errsv), errsv, NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""), @@ -9402,34 +9664,31 @@ continue_reading: } else process_valid_msg = TRUE; - switch (netlink_protocol) { - default: - nm_assert_not_reached(); - /* fall-through */ - case NETLINK_ROUTE: - /* check whether the seq number is different from before, and - * whether the previous number (@nlh_seq_last_seen) is a pending - * refresh-all request. In that case, the pending request is thereby - * completed. - * - * We must do that before processing the message with _rtnl_handle_msg(), - * because we must track the completion of the pending request before that. */ - event_seq_check_refresh_all(platform, seq_number); + /* check whether the seq number is different from before, and + * whether the previous number (@nlh_seq_last_seen) is a pending + * refresh-all request. In that case, the pending request is thereby + * completed. + * + * We must do that before processing the message with _rtnl_handle_msg(), + * because we must track the completion of the pending request before that. */ + event_seq_check_refresh_all(platform, netlink_protocol, seq_number); - if (process_valid_msg) { + if (process_valid_msg) { + if (handle_events) { /* Valid message (not checking for MULTIPART bit to * get along with broken kernels. NL_SKIP has no * effect on this. */ - - _rtnl_handle_msg(platform, &msg, handle_events); - - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + if (netlink_protocol == NMP_NETLINK_ROUTE) { + _rtnl_handle_msg(platform, &msg); + } else { + _genl_handle_msg(platform, pktinfo_group, &msg); + } } - - event_seq_check(platform, seq_number, seq_result, extack_msg); - break; + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } + event_seq_check(platform, netlink_protocol, seq_number, seq_result, extack_msg); + if (retval != 0) goto stop; @@ -9457,7 +9716,9 @@ stop: /*****************************************************************************/ static gboolean -event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) +event_handler_read_netlink(NMPlatform *platform, + NMPNetlinkProtocol netlink_protocol, + gboolean wait_for_acks) { nm_auto_pop_netns NMPNetns *netns = NULL; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); @@ -9471,8 +9732,11 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) gint64 now_nsec; } next; + nmp_netlink_protocol_check(netlink_protocol); + if (!nm_platform_netns_push(platform, &netns)) { delayed_action_wait_for_nl_response_complete_all(platform, + netlink_protocol, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_SETNS); return FALSE; } @@ -9481,20 +9745,24 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) for (;;) { int nle; - nle = _netlink_recv_handle(platform, NETLINK_ROUTE, TRUE); + nle = _netlink_recv_handle(platform, netlink_protocol, TRUE); if (nle < 0) { switch (nle) { case -EAGAIN: goto after_read; case -NME_NL_DUMP_INTR: - _LOGD("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", + _LOGD("netlink[%s]: read: uncritical failure to retrieve incoming events: %s " + "(%d)", + nmp_netlink_protocol_info(netlink_protocol)->name, nm_strerror(nle), nle); break; case -NME_NL_MSG_TRUNC: case -ENOBUFS: - _LOGI("netlink: read: %s. Need to resynchronize platform cache", ({ + _LOGI("netlink[%s]: read: %s. Need to resynchronize platform cache", + nmp_netlink_protocol_info(netlink_protocol)->name, + ({ const char *_reason = "unknown"; switch (nle) { case -NME_NL_MSG_TRUNC: @@ -9506,24 +9774,16 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) } _reason; })); - _netlink_recv_handle(platform, NETLINK_ROUTE, FALSE); + _netlink_recv_handle(platform, netlink_protocol, FALSE); delayed_action_wait_for_nl_response_complete_all( platform, + netlink_protocol, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC); - - delayed_action_schedule(platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS - | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); + delayed_action_schedule_refresh_all(platform, netlink_protocol); break; default: - _LOGE("netlink: read: failed to retrieve incoming events: %s (%d)", + _LOGE("netlink[%s]: read: failed to retrieve incoming events: %s (%d)", + nmp_netlink_protocol_info(netlink_protocol)->name, nm_strerror(nle), nle); break; @@ -9534,17 +9794,22 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) after_read: - if (!NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) + if (!NM_FLAGS_ANY( + priv->delayed_action.flags, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response)) return any; - delayed_action_wait_for_nl_response_complete_check(platform, - WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, - &next.seq_number, - &next.timeout_abs_nsec, - &next.now_nsec); + delayed_action_wait_for_response_complete_check(platform, + netlink_protocol, + WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN, + &next.seq_number, + &next.timeout_abs_nsec, + &next.now_nsec); if (!wait_for_acks - || !NM_FLAGS_HAS(priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) + || !NM_FLAGS_ANY( + priv->delayed_action.flags, + nmp_netlink_protocol_info(netlink_protocol)->delayed_action_type_wait_for_response)) return any; nm_assert(next.seq_number); @@ -9580,6 +9845,7 @@ after_read: _LOGE("netlink: read: poll failed with %s", nm_strerror_native(errsv)); delayed_action_wait_for_nl_response_complete_all( platform, + netlink_protocol, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL); return any; } @@ -9590,6 +9856,27 @@ after_read: /*****************************************************************************/ +static guint16 +genl_get_family_id(NMPlatform *platform, NMPGenlFamilyType family_type) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + + nm_assert(_NM_INT_NOT_NEGATIVE(family_type)); + nm_assert(family_type < G_N_ELEMENTS(priv->genl_family_data)); + + if (priv->genl_family_data[family_type].family_id == 0) { + /* Unknown family ID. Try to read the netlink socket, maybe we have + * a message there to learn it. */ + delayed_action_schedule(platform, DELAYED_ACTION_TYPE_READ_GENL, NULL); + if (priv->delayed_action.is_handling == 0) + delayed_action_handle_all(platform); + } + + return priv->genl_family_data[family_type].family_id; +} + +/*****************************************************************************/ + static void cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice) { @@ -9730,7 +10017,9 @@ nm_linux_platform_init(NMLinuxPlatform *self) priv->delayed_action.list_master_connected = g_ptr_array_new(); priv->delayed_action.list_refresh_link = g_ptr_array_new(); - priv->delayed_action.list_wait_for_nl_response = + priv->delayed_action.list_wait_for_response_rtnl = + g_array_new(FALSE, TRUE, sizeof(DelayedActionWaitForNlResponseData)); + priv->delayed_action.list_wait_for_response_genl = g_array_new(FALSE, TRUE, sizeof(DelayedActionWaitForNlResponseData)); } @@ -9773,6 +10062,32 @@ constructed(GObject *_object) /*************************************************************************/ + /* disable MSG_PEEK, we will handle lost messages ourselves. */ + nle = nl_socket_new(&priv->sk_genl, + NETLINK_GENERIC, + NL_SOCKET_FLAGS_NONBLOCK | NL_SOCKET_FLAGS_PASSCRED + | NL_SOCKET_FLAGS_DISABLE_MSG_PEEK, + 8 * 1024 * 1024, + 0); + g_assert(!nle); + + nle = nl_socket_add_memberships(priv->sk_genl, GENL_ID_CTRL, 0); + g_assert(!nle); + + fd = nl_socket_get_fd(priv->sk_genl); + + _LOGD("genl: generic netlink socket created: port=%u, fd=%d", + nl_socket_get_local_port(priv->sk_genl), + fd); + + priv->event_source_genl = + nm_g_unix_fd_add_source(fd, + G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, + _nl_event_handler_genl, + platform); + + /*************************************************************************/ + /* disable MSG_PEEK, we will handle lost messages ourselves. */ nle = nl_socket_new(&priv->sk_rtnl, NETLINK_ROUTE, @@ -9804,10 +10119,10 @@ constructed(GObject *_object) nl_socket_get_local_port(priv->sk_rtnl), fd); - priv->rtnl_event_source = + priv->event_source_rtnl = nm_g_unix_fd_add_source(fd, G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, - rtnl_event_handler, + _nl_event_handler_rtnl, platform); /*************************************************************************/ @@ -9816,17 +10131,10 @@ constructed(GObject *_object) G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object); _LOGD("populate platform cache"); - delayed_action_schedule( - platform, - DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES - | DELAYED_ACTION_TYPE_REFRESH_ALL_ROUTING_RULES_ALL - | DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS | DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS, - NULL); + delayed_action_schedule_refresh_all(platform, NMP_NETLINK_ROUTE); + delayed_action_schedule_refresh_all(platform, NMP_NETLINK_GENERIC); - delayed_action_handle_all(platform, FALSE); + delayed_action_handle_all(platform); /* Set up udev monitoring */ if (priv->udev_client) { @@ -9908,6 +10216,11 @@ dispose(GObject *object) _LOGD("dispose"); delayed_action_wait_for_nl_response_complete_all(platform, + NMP_NETLINK_GENERIC, + WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); + + delayed_action_wait_for_nl_response_complete_all(platform, + NMP_NETLINK_ROUTE, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING); priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE; @@ -9924,12 +10237,14 @@ finalize(GObject *object) g_ptr_array_unref(priv->delayed_action.list_master_connected); g_ptr_array_unref(priv->delayed_action.list_refresh_link); - g_array_unref(priv->delayed_action.list_wait_for_nl_response); + g_array_unref(priv->delayed_action.list_wait_for_response_rtnl); + g_array_unref(priv->delayed_action.list_wait_for_response_genl); + + nm_clear_g_source_inst(&priv->event_source_genl); + nm_clear_g_source_inst(&priv->event_source_rtnl); nl_socket_free(priv->sk_genl_sync); - - nm_clear_g_source_inst(&priv->rtnl_event_source); - + nl_socket_free(priv->sk_genl); nl_socket_free(priv->sk_rtnl); { @@ -10048,4 +10363,6 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->tfilter_delete = tfilter_delete; platform_class->process_events = process_events; + + platform_class->genl_get_family_id = genl_get_family_id; } diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c index 77fffc0bff..cb5bd4a896 100644 --- a/src/libnm-platform/nm-netlink.c +++ b/src/libnm-platform/nm-netlink.c @@ -38,7 +38,7 @@ struct nl_msg { struct sockaddr_nl nm_dst; struct ucred nm_creds; struct nlmsghdr *nm_nlh; - size_t nm_size; + uint32_t nm_size; bool nm_creds_has : 1; }; @@ -261,23 +261,26 @@ nlmsg_hdr(struct nl_msg *n) } void * -nlmsg_reserve(struct nl_msg *n, size_t len, int pad) +nlmsg_reserve(struct nl_msg *n, uint32_t len, uint32_t pad) { - char *buf = (char *) n->nm_nlh; - size_t nlmsg_len = n->nm_nlh->nlmsg_len; - size_t tlen; + char *buf = (char *) n->nm_nlh; + uint32_t tlen; - nm_assert(pad >= 0); + nm_assert(n); + nm_assert(pad == 0 || nm_utils_is_power_of_two(pad)); + nm_assert(n->nm_nlh->nlmsg_len <= n->nm_size); - if (len > n->nm_size) + if (pad != 0) { + tlen = (len + (pad - 1u)) & ~(pad - 1u); + if (tlen < len) + return NULL; + } else + tlen = len; + + if (tlen > n->nm_size - n->nm_nlh->nlmsg_len) return NULL; - tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; - - if ((tlen + nlmsg_len) > n->nm_size) - return NULL; - - buf += nlmsg_len; + buf += n->nm_nlh->nlmsg_len; n->nm_nlh->nlmsg_len += tlen; if (tlen > len) @@ -322,6 +325,8 @@ nlmsg_alloc_size(size_t len) if (len < sizeof(struct nlmsghdr)) len = sizeof(struct nlmsghdr); + else if (len > UINT32_MAX) + g_return_val_if_reached(NULL); nm = g_slice_new(struct nl_msg); *nm = (struct nl_msg){ @@ -359,7 +364,7 @@ nlmsg_alloc_convert(struct nlmsghdr *hdr) } struct nl_msg * -nlmsg_alloc_simple(int nlmsgtype, int flags) +nlmsg_alloc_simple(uint16_t nlmsgtype, uint16_t flags) { struct nl_msg *nm; struct nlmsghdr *new; @@ -384,20 +389,20 @@ nlmsg_free(struct nl_msg *msg) /*****************************************************************************/ int -nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad) +nlmsg_append(struct nl_msg *n, const void *data, uint32_t len, uint32_t pad) { void *tmp; nm_assert(n); - nm_assert(data); - nm_assert(len > 0); - nm_assert(pad >= 0); + nm_assert(len == 0 || data); tmp = nlmsg_reserve(n, len, pad); - if (tmp == NULL) + if (!tmp) return -ENOMEM; - memcpy(tmp, data, len); + if (len > 0) + memcpy(tmp, data, len); + return 0; } @@ -417,14 +422,17 @@ nlmsg_parse(const struct nlmsghdr *nlh, } struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags) +nlmsg_put(struct nl_msg *n, + uint32_t pid, + uint32_t seq, + uint16_t type, + uint32_t payload, + uint16_t flags) { - struct nlmsghdr *nlh; + struct nlmsghdr *nlh = (struct nlmsghdr *) n->nm_nlh; - if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) - g_return_val_if_reached(NULL); + nm_assert(nlh->nlmsg_len >= NLMSG_HDRLEN); - nlh = (struct nlmsghdr *) n->nm_nlh; nlh->nlmsg_type = type; nlh->nlmsg_flags = flags; nlh->nlmsg_pid = pid; @@ -565,7 +573,8 @@ nla_nest_start(struct nl_msg *msg, int attrtype) static int _nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) { - size_t pad, len; + size_t len; + uint32_t pad; len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; @@ -584,14 +593,18 @@ _nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; if (pad > 0) { + void *p; + /* * Data inside attribute does not end at a alignment boundary. * Pad accordingly and account for the additional space in * the message. nlmsg_reserve() may never fail in this situation, * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. */ - if (!nlmsg_reserve(msg, pad, 0)) + p = nlmsg_reserve(msg, pad, 0); + if (!p) g_return_val_if_reached(-NME_BUG); + memset(p, 0, pad); } return 0; @@ -730,9 +743,9 @@ void * genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, - int family, - int hdrlen, - int flags, + uint16_t family, + uint32_t hdrlen, + uint16_t flags, uint8_t cmd, uint8_t version) { diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h index efc9585313..852c9c8f6d 100644 --- a/src/libnm-platform/nm-netlink.h +++ b/src/libnm-platform/nm-netlink.h @@ -60,7 +60,7 @@ struct nl_msg_lite { const struct sockaddr_nl *nm_dst; const struct ucred *nm_creds; const struct nlmsghdr *nm_nlh; - size_t nm_size; + uint32_t nm_size; }; /*****************************************************************************/ @@ -389,13 +389,13 @@ struct nl_msg *nlmsg_alloc_size(size_t max); struct nl_msg *nlmsg_alloc_convert(struct nlmsghdr *hdr); -struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags); +struct nl_msg *nlmsg_alloc_simple(uint16_t nlmsgtype, uint16_t flags); -void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad); +void *nlmsg_reserve(struct nl_msg *n, uint32_t len, uint32_t pad); -int nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad); +int nlmsg_append(struct nl_msg *n, const void *data, uint32_t len, uint32_t pad); -#define nlmsg_append_struct(n, data) nlmsg_append(n, (data), sizeof(*(data)), NLMSG_ALIGNTO) +#define nlmsg_append_struct(n, data) (nlmsg_append((n), (data), sizeof(*(data)), NLMSG_ALIGNTO)) void nlmsg_free(struct nl_msg *msg); @@ -502,8 +502,12 @@ int nlmsg_parse(const struct nlmsghdr *nlh, nlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ }) -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags); +struct nlmsghdr *nlmsg_put(struct nl_msg *n, + uint32_t pid, + uint32_t seq, + uint16_t type, + uint32_t payload, + uint16_t flags); /*****************************************************************************/ @@ -614,9 +618,9 @@ extern const struct nla_policy genl_ctrl_policy[8]; void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, - int family, - int hdrlen, - int flags, + uint16_t family, + uint32_t hdrlen, + uint16_t flags, uint8_t cmd, uint8_t version); void *genlmsg_data(const struct genlmsghdr *gnlh); diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 50204abc5f..961c617cc6 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -395,6 +395,76 @@ _nm_platform_kernel_support_init(NMPlatformKernelSupportType type, int value) /*****************************************************************************/ +const NMPGenlFamilyInfo nmp_genl_family_infos[_NMP_GENL_FAMILY_TYPE_NUM] = { + [NMP_GENL_FAMILY_TYPE_ETHTOOL] = + { + .name = "ethtool", + }, + [NMP_GENL_FAMILY_TYPE_MPTCP_PM] = + { + .name = "mptcp_pm", + }, + [NMP_GENL_FAMILY_TYPE_NL80211] = + { + .name = "nl80211", + }, + [NMP_GENL_FAMILY_TYPE_NL802154] = + { + .name = "nl802154", + }, + [NMP_GENL_FAMILY_TYPE_WIREGUARD] = + { + .name = "wireguard", + }, +}; + +NMPGenlFamilyType +nmp_genl_family_type_from_name(const char *name) +{ + int imin, imax, imid; + + if (NM_MORE_ASSERT_ONCE(50)) { + int i; + + for (i = 0; i < (int) G_N_ELEMENTS(nmp_genl_family_infos); i++) { + nm_assert(nmp_genl_family_infos[i].name); + if (i > 0) + nm_assert(strcmp(nmp_genl_family_infos[i - 1].name, nmp_genl_family_infos[i].name) + < 0); + } + } + + if (!name) + goto out; + + imin = 0; + imax = G_N_ELEMENTS(nmp_genl_family_infos) - 1; + imid = imax / 2; + + while (TRUE) { + int c; + + c = strcmp(nmp_genl_family_infos[imid].name, name); + if (c == 0) + return (NMPGenlFamilyType) imid; + + if (c < 0) + imin = imid + 1; + else + imax = imid - 1; + + if (imin > imax) + break; + + imid = (imax + imin) / 2; + } + +out: + return _NMP_GENL_FAMILY_TYPE_NONE; +} + +/*****************************************************************************/ + /** * nm_platform_process_events: * @self: platform instance @@ -8975,6 +9045,19 @@ nm_platform_ip_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatform /*****************************************************************************/ +guint16 +nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type) +{ + _CHECK_SELF(self, klass, 0); + + if (!_NM_INT_NOT_NEGATIVE(family_type) || family_type >= _NMP_GENL_FAMILY_TYPE_NUM) + g_return_val_if_reached(0); + + return klass->genl_get_family_id(self, family_type); +} + +/*****************************************************************************/ + GHashTable * nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex) { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index b921a2f562..984ad79cba 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -1103,6 +1103,25 @@ nm_platform_kernel_support_get(NMPlatformKernelSupportType type) return nm_platform_kernel_support_get_full(type, TRUE) != NM_OPTION_BOOL_FALSE; } +typedef enum { + NMP_GENL_FAMILY_TYPE_ETHTOOL, + NMP_GENL_FAMILY_TYPE_MPTCP_PM, + NMP_GENL_FAMILY_TYPE_NL80211, + NMP_GENL_FAMILY_TYPE_NL802154, + NMP_GENL_FAMILY_TYPE_WIREGUARD, + + _NMP_GENL_FAMILY_TYPE_NUM, + _NMP_GENL_FAMILY_TYPE_NONE = _NMP_GENL_FAMILY_TYPE_NUM, +} NMPGenlFamilyType; + +typedef struct { + const char *name; +} NMPGenlFamilyInfo; + +extern const NMPGenlFamilyInfo nmp_genl_family_infos[_NMP_GENL_FAMILY_TYPE_NUM]; + +NMPGenlFamilyType nmp_genl_family_type_from_name(const char *name); + /*****************************************************************************/ struct _NMPlatformPrivate; @@ -1309,6 +1328,9 @@ typedef struct { int (*tfilter_add)(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter); int (*tfilter_delete)(NMPlatform *self, int ifindex, guint32 parent, gboolean log_error); + + guint16 (*genl_get_family_id)(NMPlatform *platform, NMPGenlFamilyType family_type); + } NMPlatformClass; /* NMPlatform signals @@ -2512,4 +2534,8 @@ gboolean nm_platform_ip_address_match(int addr_family, const NMPlatformIPAddress *addr, NMPlatformMatchFlags match_flag); +/*****************************************************************************/ + +guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type); + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index 99e2287691..d411043d0f 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -1149,10 +1149,7 @@ _vt_cmd_obj_hash_update_link(const NMPObject *obj, NMHashState *h) nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_LINK); nm_platform_link_hash_update(&obj->link, h); - nm_hash_update_vals(h, - obj->_link.netlink.is_in_netlink, - obj->_link.wireguard_family_id, - obj->_link.udev.device); + nm_hash_update_vals(h, obj->_link.netlink.is_in_netlink, obj->_link.udev.device); if (obj->_link.netlink.lnk) nmp_object_hash_update(obj->_link.netlink.lnk, h); } @@ -1232,7 +1229,6 @@ _vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2) NM_CMP_RETURN(nm_platform_link_cmp(&obj1->link, &obj2->link)); NM_CMP_DIRECT(obj1->_link.netlink.is_in_netlink, obj2->_link.netlink.is_in_netlink); NM_CMP_RETURN(nmp_object_cmp(obj1->_link.netlink.lnk, obj2->_link.netlink.lnk)); - NM_CMP_DIRECT(obj1->_link.wireguard_family_id, obj2->_link.wireguard_family_id); if (obj1->_link.udev.device != obj2->_link.udev.device) { if (!obj1->_link.udev.device) diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index d0c0463f13..7d2ebf979a 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -238,11 +238,6 @@ typedef struct { /* Auxiliary data object for Wi-Fi and WPAN */ GObject *ext_data; - - /* FIXME: not every NMPObjectLink should pay the price for tracking - * the wireguard family id. This should be tracked via ext_data, which - * would be exactly the right place. */ - int wireguard_family_id; } NMPObjectLink; typedef struct { diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c index 475b8a390a..1875a42068 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c @@ -46,9 +46,9 @@ typedef struct { NMWifiUtils parent; struct nl_sock *nl_sock; guint32 *freqs; - int id; int num_freqs; int phy; + guint16 genl_family_id; bool can_wowlan : 1; } NMWifiUtilsNl80211; @@ -83,12 +83,12 @@ error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) } static struct nl_msg * -_nl80211_alloc_msg(int id, int ifindex, int phy, guint32 cmd, guint32 flags) +_nl80211_alloc_msg(guint16 genl_family_id, int ifindex, int phy, uint8_t cmd, uint16_t flags) { nm_auto_nlmsg struct nl_msg *msg = NULL; msg = nlmsg_alloc(); - genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0); + genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, genl_family_id, 0, flags, cmd, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); if (phy != -1) NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, phy); @@ -99,9 +99,9 @@ nla_put_failure: } static struct nl_msg * -nl80211_alloc_msg(NMWifiUtilsNl80211 *self, guint32 cmd, guint32 flags) +nl80211_alloc_msg(NMWifiUtilsNl80211 *self, uint8_t cmd, uint16_t flags) { - return _nl80211_alloc_msg(self->id, self->parent.ifindex, self->phy, cmd, flags); + return _nl80211_alloc_msg(self->genl_family_id, self->parent.ifindex, self->phy, cmd, flags); } static int @@ -945,7 +945,7 @@ nm_wifi_utils_nl80211_class_init(NMWifiUtilsNl80211Class *klass) } NMWifiUtils * -nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl) +nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex) { gs_unref_object NMWifiUtilsNl80211 *self = NULL; nm_auto_nlmsg struct nl_msg *msg = NULL; @@ -954,16 +954,14 @@ nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl) if (!genl) return NULL; + if (genl_family_id == 0) + return NULL; + self = g_object_new(NM_TYPE_WIFI_UTILS_NL80211, NULL); self->parent.ifindex = ifindex; self->nl_sock = genl; - - self->id = genl_ctrl_resolve(self->nl_sock, "nl80211"); - if (self->id < 0) { - _LOGD("genl_ctrl_resolve: failed to resolve \"nl80211\""); - return NULL; - } + self->genl_family_id = genl_family_id; self->phy = -1; diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h index 4a63330739..4783c572b4 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.h @@ -24,6 +24,6 @@ GType nm_wifi_utils_nl80211_get_type(void); -NMWifiUtils *nm_wifi_utils_nl80211_new(int ifindex, struct nl_sock *genl); +NMWifiUtils *nm_wifi_utils_nl80211_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex); #endif /* __WIFI_UTILS_NL80211_H__ */ diff --git a/src/libnm-platform/wifi/nm-wifi-utils.c b/src/libnm-platform/wifi/nm-wifi-utils.c index 08a8ec4ff3..0238b74aa9 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.c +++ b/src/libnm-platform/wifi/nm-wifi-utils.c @@ -32,16 +32,16 @@ nm_wifi_utils_class_init(NMWifiUtilsClass *klass) {} NMWifiUtils * -nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan) +nm_wifi_utils_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex, gboolean check_scan) { NMWifiUtils *ret; g_return_val_if_fail(ifindex > 0, NULL); - ret = nm_wifi_utils_nl80211_new(ifindex, genl); + ret = nm_wifi_utils_nl80211_new(genl, genl_family_id, ifindex); #if HAVE_WEXT - if (ret == NULL) + if (!ret) ret = nm_wifi_utils_wext_new(ifindex, check_scan); #endif diff --git a/src/libnm-platform/wifi/nm-wifi-utils.h b/src/libnm-platform/wifi/nm-wifi-utils.h index 0d30c1a119..4dbeb21ff3 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -27,7 +27,8 @@ GType nm_wifi_utils_get_type(void); gboolean nm_wifi_utils_is_wifi(int dirfd, const char *ifname); -NMWifiUtils *nm_wifi_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan); +NMWifiUtils * +nm_wifi_utils_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex, gboolean check_scan); _NMDeviceWifiCapabilities nm_wifi_utils_get_caps(NMWifiUtils *data); diff --git a/src/libnm-platform/wpan/nm-wpan-utils.c b/src/libnm-platform/wpan/nm-wpan-utils.c index d9bbd0f0fa..0afbe58d3e 100644 --- a/src/libnm-platform/wpan/nm-wpan-utils.c +++ b/src/libnm-platform/wpan/nm-wpan-utils.c @@ -37,9 +37,9 @@ struct NMWpanUtils { GObject parent; - int ifindex; struct nl_sock *nl_sock; - int id; + int ifindex; + guint16 genl_family_id; }; typedef struct { @@ -75,12 +75,12 @@ error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) } static struct nl_msg * -_nl802154_alloc_msg(int id, int ifindex, guint32 cmd, guint32 flags) +_nl802154_alloc_msg(guint16 genl_family_id, int ifindex, uint8_t cmd, uint16_t flags) { nm_auto_nlmsg struct nl_msg *msg = NULL; msg = nlmsg_alloc(); - genlmsg_put(msg, 0, 0, id, 0, flags, cmd, 0); + genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, genl_family_id, 0, flags, cmd, 0); NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, ifindex); return g_steal_pointer(&msg); @@ -89,9 +89,9 @@ nla_put_failure: } static struct nl_msg * -nl802154_alloc_msg(NMWpanUtils *self, guint32 cmd, guint32 flags) +nl802154_alloc_msg(NMWpanUtils *self, uint8_t cmd, uint16_t flags) { - return _nl802154_alloc_msg(self->id, self->ifindex, cmd, flags); + return _nl802154_alloc_msg(self->genl_family_id, self->ifindex, cmd, flags); } static int @@ -264,7 +264,7 @@ nm_wpan_utils_class_init(NMWpanUtilsClass *klass) {} NMWpanUtils * -nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan) +nm_wpan_utils_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex, gboolean check_scan) { NMWpanUtils *self; @@ -273,16 +273,13 @@ nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan) if (!genl) return NULL; - self = g_object_new(NM_TYPE_WPAN_UTILS, NULL); - self->ifindex = ifindex; - self->nl_sock = genl; - self->id = genl_ctrl_resolve(genl, "nl802154"); - - if (self->id < 0) { - _LOGD(LOGD_PLATFORM, "genl_ctrl_resolve: failed to resolve \"nl802154\""); - g_object_unref(self); + if (!genl_family_id) return NULL; - } + + self = g_object_new(NM_TYPE_WPAN_UTILS, NULL); + self->ifindex = ifindex; + self->nl_sock = genl; + self->genl_family_id = genl_family_id; return self; } diff --git a/src/libnm-platform/wpan/nm-wpan-utils.h b/src/libnm-platform/wpan/nm-wpan-utils.h index 6130c41e56..885d7195a8 100644 --- a/src/libnm-platform/wpan/nm-wpan-utils.h +++ b/src/libnm-platform/wpan/nm-wpan-utils.h @@ -23,7 +23,8 @@ typedef struct NMWpanUtils NMWpanUtils; GType nm_wpan_utils_get_type(void); -NMWpanUtils *nm_wpan_utils_new(int ifindex, struct nl_sock *genl, gboolean check_scan); +NMWpanUtils * +nm_wpan_utils_new(struct nl_sock *genl, guint16 genl_family_id, int ifindex, gboolean check_scan); guint16 nm_wpan_utils_get_pan_id(NMWpanUtils *self); gboolean nm_wpan_utils_set_pan_id(NMWpanUtils *self, guint16 pan_id);