diff options
| author | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-08-16 18:24:19 +0200 |
| commit | 0018d1f3cf71d680d7b6bceda55a5717244d8b26 (patch) | |
| tree | a058f1d106d172d3354179437ef034c9355cdf9c /src/libnm-platform | |
| parent | 6accbd3ec0e42d8633bbde4d47ed7bfe854e7e0b (diff) | |
New upstream version 1.39.90 upstream/1.39.90
Diffstat (limited to 'src/libnm-platform')
| -rw-r--r-- | src/libnm-platform/meson.build | 2 | ||||
| -rw-r--r-- | src/libnm-platform/nm-linux-platform.c | 2328 | ||||
| -rw-r--r-- | src/libnm-platform/nm-netlink.c | 654 | ||||
| -rw-r--r-- | src/libnm-platform/nm-netlink.h | 170 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform.c | 696 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform.h | 230 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-base.h | 3 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-global-tracker.c | 1261 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-global-tracker.h | 84 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-object.c | 204 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-object.h | 31 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-route-manager.c | 844 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-route-manager.h | 79 | ||||
| -rw-r--r-- | src/libnm-platform/tests/test-nm-platform.c | 5 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils-nl80211.c | 45 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils-nl80211.h | 2 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils.c | 6 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils.h | 5 | ||||
| -rw-r--r-- | src/libnm-platform/wpan/nm-wpan-utils.c | 40 | ||||
| -rw-r--r-- | src/libnm-platform/wpan/nm-wpan-utils.h | 5 |
20 files changed, 4432 insertions, 2262 deletions
diff --git a/src/libnm-platform/meson.build b/src/libnm-platform/meson.build index d0a7c6c2..e273046f 100644 --- a/src/libnm-platform/meson.build +++ b/src/libnm-platform/meson.build @@ -7,9 +7,9 @@ libnm_platform = static_library( 'nm-netlink.c', 'nm-platform-utils.c', 'nm-platform.c', + 'nmp-global-tracker.c', 'nmp-netns.c', 'nmp-object.c', - 'nmp-route-manager.c', 'wifi/nm-wifi-utils-nl80211.c', 'wifi/nm-wifi-utils.c', 'wpan/nm-wpan-utils.c', diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 40cb4396..cd685fe2 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -7,12 +7,13 @@ #include "nm-linux-platform.h" +#include "libnm-std-aux/nm-linux-compat.h" + #include <arpa/inet.h> #include <dlfcn.h> #include <endian.h> #include <fcntl.h> #include <libudev.h> -#include <net/ethernet.h> #include <linux/fib_rules.h> #include <linux/ip.h> #include <linux/if.h> @@ -50,6 +51,14 @@ /*****************************************************************************/ +G_STATIC_ASSERT(NM_MPTCP_PM_ADDR_FLAG_SIGNAL == MPTCP_PM_ADDR_FLAG_SIGNAL); +G_STATIC_ASSERT(NM_MPTCP_PM_ADDR_FLAG_SUBFLOW == MPTCP_PM_ADDR_FLAG_SUBFLOW); +G_STATIC_ASSERT(NM_MPTCP_PM_ADDR_FLAG_BACKUP == MPTCP_PM_ADDR_FLAG_BACKUP); +G_STATIC_ASSERT(NM_MPTCP_PM_ADDR_FLAG_FULLMESH == MPTCP_PM_ADDR_FLAG_FULLMESH); +G_STATIC_ASSERT(NM_MPTCP_PM_ADDR_FLAG_IMPLICIT == MPTCP_PM_ADDR_FLAG_IMPLICIT); + +/*****************************************************************************/ + /* re-implement <linux/tc_act/tc_defact.h> to build against kernel * headers that lack this. */ @@ -79,6 +88,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 @@ -149,6 +177,12 @@ G_STATIC_ASSERT(RTA_MAX == (__RTA_MAX - 1)); /*****************************************************************************/ +#define IFLA_BOND_PEER_NOTIF_DELAY 28 + +#undef IFLA_BOND_MAX + +/*****************************************************************************/ + /* Appeared in the kernel prior to 3.13 dated 19 January, 2014 */ #ifndef ARPHRD_6LOWPAN #define ARPHRD_6LOWPAN 825 @@ -279,6 +313,12 @@ struct _ifla_vf_vlan_info { /*****************************************************************************/ +typedef struct { + guint16 family_id; +} GenlFamilyData; + +/*****************************************************************************/ + typedef enum { INFINIBAND_ACTION_CREATE_CHILD, INFINIBAND_ACTION_DELETE_CHILD, @@ -302,15 +342,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 +360,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 +447,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 *genl; - - struct nl_sock *nlh; - - GSource *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 +518,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 +540,8 @@ typedef struct { gsize len; } netlink_recv_buf; + GenlFamilyData genl_family_data[_NMP_GENL_FAMILY_TYPE_NUM]; + } NMLinuxPlatformPrivate; struct _NMLinuxPlatform { @@ -562,7 +642,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,8 +650,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 struct nl_sock *_genl_sock(NMLinuxPlatform *platform); +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))]) /*****************************************************************************/ @@ -1434,6 +1542,152 @@ _parse_lnk_bridge(const char *kind, struct nlattr *info_data) /***********************************************************************************/ static NMPObject * +_parse_lnk_bond(const char *kind, struct nlattr *info_data) +{ + static const struct nla_policy policy[] = { + [IFLA_BOND_MODE] = {.type = NLA_U8}, + [IFLA_BOND_ACTIVE_SLAVE] = {.type = NLA_U32}, + [IFLA_BOND_MIIMON] = {.type = NLA_U32}, + [IFLA_BOND_UPDELAY] = {.type = NLA_U32}, + [IFLA_BOND_DOWNDELAY] = {.type = NLA_U32}, + [IFLA_BOND_USE_CARRIER] = {.type = NLA_U8}, + [IFLA_BOND_ARP_INTERVAL] = {.type = NLA_U32}, + [IFLA_BOND_ARP_IP_TARGET] = {.type = NLA_NESTED}, + [IFLA_BOND_ARP_VALIDATE] = {.type = NLA_U32}, + [IFLA_BOND_ARP_ALL_TARGETS] = {.type = NLA_U32}, + [IFLA_BOND_PRIMARY] = {.type = NLA_U32}, + [IFLA_BOND_PRIMARY_RESELECT] = {.type = NLA_U8}, + [IFLA_BOND_FAIL_OVER_MAC] = {.type = NLA_U8}, + [IFLA_BOND_XMIT_HASH_POLICY] = {.type = NLA_U8}, + [IFLA_BOND_RESEND_IGMP] = {.type = NLA_U32}, + [IFLA_BOND_NUM_PEER_NOTIF] = {.type = NLA_U8}, + [IFLA_BOND_ALL_SLAVES_ACTIVE] = {.type = NLA_U8}, + [IFLA_BOND_MIN_LINKS] = {.type = NLA_U32}, + [IFLA_BOND_LP_INTERVAL] = {.type = NLA_U32}, + [IFLA_BOND_PACKETS_PER_SLAVE] = {.type = NLA_U32}, + [IFLA_BOND_AD_LACP_RATE] = {.type = NLA_U8}, + [IFLA_BOND_AD_SELECT] = {.type = NLA_U8}, + [IFLA_BOND_AD_ACTOR_SYS_PRIO] = {.type = NLA_U16}, + [IFLA_BOND_AD_USER_PORT_KEY] = {.type = NLA_U16}, + [IFLA_BOND_AD_ACTOR_SYSTEM] = {.minlen = sizeof(NMEtherAddr)}, + [IFLA_BOND_TLB_DYNAMIC_LB] = {.type = NLA_U8}, + [IFLA_BOND_PEER_NOTIF_DELAY] = {.type = NLA_U32}, + }; + NMPlatformLnkBond *props; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + NMPObject *obj = NULL; + + if (!info_data || !nm_streq0(kind, "bond")) + return NULL; + + if (nla_parse_nested_arr(tb, info_data, policy) < 0) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_LNK_BOND, NULL); + + props = &obj->lnk_bond; + + if (tb[IFLA_BOND_MODE]) + props->mode = nla_get_u8(tb[IFLA_BOND_MODE]); + if (tb[IFLA_BOND_PRIMARY]) { + props->primary = nla_get_u32(tb[IFLA_BOND_PRIMARY]); + } else if (tb[IFLA_BOND_ACTIVE_SLAVE]) { + props->primary = nla_get_u32(tb[IFLA_BOND_ACTIVE_SLAVE]); + } + if (tb[IFLA_BOND_MIIMON]) { + props->miimon = nla_get_u32(tb[IFLA_BOND_MIIMON]); + props->miimon_has = TRUE; + } else { + props->miimon_has = FALSE; + } + if (tb[IFLA_BOND_UPDELAY]) { + props->updelay = nla_get_u32(tb[IFLA_BOND_UPDELAY]); + props->updelay_has = TRUE; + } else { + props->updelay_has = FALSE; + } + if (tb[IFLA_BOND_DOWNDELAY]) { + props->downdelay = nla_get_u32(tb[IFLA_BOND_DOWNDELAY]); + props->downdelay_has = TRUE; + } else { + props->downdelay_has = FALSE; + } + if (tb[IFLA_BOND_USE_CARRIER]) + props->use_carrier = nla_get_u8(tb[IFLA_BOND_USE_CARRIER]); + if (tb[IFLA_BOND_ARP_INTERVAL]) + props->arp_interval = nla_get_u32(tb[IFLA_BOND_ARP_INTERVAL]); + if (tb[IFLA_BOND_ARP_IP_TARGET]) { + struct nlattr *attr; + int rem; + + nla_for_each_nested (attr, tb[IFLA_BOND_ARP_IP_TARGET], rem) { + if (props->arp_ip_targets_num > NM_BOND_MAX_ARP_TARGETS - 1) + break; + if (nla_len(attr) < sizeof(in_addr_t)) + break; + + props->arp_ip_target[props->arp_ip_targets_num++] = nla_get_u32(attr); + } + } + if (tb[IFLA_BOND_ARP_VALIDATE]) + props->arp_validate = nla_get_u32(tb[IFLA_BOND_ARP_VALIDATE]); + if (tb[IFLA_BOND_ARP_ALL_TARGETS]) { + props->arp_all_targets = nla_get_u32(tb[IFLA_BOND_ARP_ALL_TARGETS]); + props->arp_all_targets_has = TRUE; + } else { + props->arp_all_targets_has = FALSE; + } + if (tb[IFLA_BOND_PRIMARY_RESELECT]) + props->primary_reselect = nla_get_u8(tb[IFLA_BOND_PRIMARY_RESELECT]); + if (tb[IFLA_BOND_FAIL_OVER_MAC]) + props->fail_over_mac = nla_get_u8(tb[IFLA_BOND_FAIL_OVER_MAC]); + if (tb[IFLA_BOND_XMIT_HASH_POLICY]) + props->xmit_hash_policy = nla_get_u8(tb[IFLA_BOND_XMIT_HASH_POLICY]); + if (tb[IFLA_BOND_RESEND_IGMP]) { + props->resend_igmp = nla_get_u32(tb[IFLA_BOND_RESEND_IGMP]); + props->resend_igmp_has = TRUE; + } else { + props->resend_igmp_has = FALSE; + } + if (tb[IFLA_BOND_NUM_PEER_NOTIF]) + props->num_grat_arp = nla_get_u8(tb[IFLA_BOND_NUM_PEER_NOTIF]); + if (tb[IFLA_BOND_ALL_SLAVES_ACTIVE]) + props->all_ports_active = nla_get_u8(tb[IFLA_BOND_ALL_SLAVES_ACTIVE]); + if (tb[IFLA_BOND_MIN_LINKS]) + props->min_links = nla_get_u32(tb[IFLA_BOND_MIN_LINKS]); + if (tb[IFLA_BOND_LP_INTERVAL]) + props->lp_interval = nla_get_u32(tb[IFLA_BOND_LP_INTERVAL]); + if (tb[IFLA_BOND_PACKETS_PER_SLAVE]) + props->packets_per_port = nla_get_u32(tb[IFLA_BOND_PACKETS_PER_SLAVE]); + if (tb[IFLA_BOND_AD_LACP_RATE]) + props->lacp_rate = nla_get_u8(tb[IFLA_BOND_AD_LACP_RATE]); + if (tb[IFLA_BOND_AD_SELECT]) + props->ad_select = nla_get_u8(tb[IFLA_BOND_AD_SELECT]); + if (tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]) + props->ad_actor_sys_prio = nla_get_u16(tb[IFLA_BOND_AD_ACTOR_SYS_PRIO]); + if (tb[IFLA_BOND_AD_USER_PORT_KEY]) + props->ad_user_port_key = nla_get_u16(tb[IFLA_BOND_AD_USER_PORT_KEY]); + if (tb[IFLA_BOND_AD_ACTOR_SYSTEM]) + props->ad_actor_system = *nla_data_as(NMEtherAddr, tb[IFLA_BOND_AD_ACTOR_SYSTEM]); + if (tb[IFLA_BOND_TLB_DYNAMIC_LB]) { + props->tlb_dynamic_lb = nla_get_u8(tb[IFLA_BOND_TLB_DYNAMIC_LB]); + props->tlb_dynamic_lb_has = TRUE; + } else { + props->tlb_dynamic_lb_has = FALSE; + } + if (tb[IFLA_BOND_PEER_NOTIF_DELAY]) { + props->peer_notif_delay = nla_get_u32(tb[IFLA_BOND_PEER_NOTIF_DELAY]); + props->peer_notif_delay_has = TRUE; + } else { + props->peer_notif_delay_has = FALSE; + } + + return obj; +} + +/***********************************************************************************/ + +static NMPObject * _parse_lnk_gre(const char *kind, struct nlattr *info_data) { static const struct nla_policy policy[] = { @@ -2322,7 +2576,7 @@ typedef struct { } WireGuardParseData; static int -_wireguard_get_device_cb(struct nl_msg *msg, void *arg) +_wireguard_get_device_cb(const struct nl_msg *msg, void *arg) { static const struct nla_policy policy[] = { [WGDEVICE_A_IFINDEX] = {.type = NLA_U32}, @@ -2400,7 +2654,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; @@ -2414,10 +2668,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 0x%x)...", ifindex, wireguard_family_id); @@ -2530,25 +2786,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->genl, "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; @@ -2558,7 +2797,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); @@ -2573,7 +2812,7 @@ _wireguard_refresh_link(NMPlatform *platform, int wireguard_family_id, int ifind if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) lnk_new = nmp_object_ref(plink->_link.netlink.lnk); } else { - lnk_new = _wireguard_read_info(platform, priv->genl, wireguard_family_id, ifindex); + lnk_new = _wireguard_read_info(platform, priv->sk_genl_sync, wireguard_family_id, ifindex); if (!lnk_new) { if (NMP_OBJECT_GET_TYPE(plink->_link.netlink.lnk) == NMP_OBJECT_TYPE_LNK_WIREGUARD) lnk_new = nmp_object_ref(plink->_link.netlink.lnk); @@ -2583,15 +2822,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; @@ -2617,7 +2854,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, @@ -2636,14 +2873,6 @@ _wireguard_create_change_nlmsgs(NMPlatform *platfo struct nlattr *nest_curr_allowed_ip; NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT; -#define _nla_nest_end(msg, nest_start) \ - G_STMT_START \ - { \ - if (nla_nest_end((msg), (nest_start)) < 0) \ - g_return_val_if_reached(-NME_BUG); \ - } \ - G_STMT_END - /* Adapted from LGPL-2.1+ code [1]. * * [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */ @@ -2799,36 +3028,36 @@ again: if (nla_put_uint8(msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0) goto toobig_allowedips; - _nla_nest_end(msg, nest_curr_allowed_ip); + NLA_NEST_END(msg, nest_curr_allowed_ip); nest_curr_allowed_ip = NULL; } idx_allowed_ips_curr = IDX_NIL; - _nla_nest_end(msg, nest_allowed_ips); + NLA_NEST_END(msg, nest_allowed_ips); nest_allowed_ips = NULL; } } - _nla_nest_end(msg, nest_curr_peer); + NLA_NEST_END(msg, nest_curr_peer); nest_curr_peer = NULL; } - _nla_nest_end(msg, nest_peers); + NLA_NEST_END(msg, nest_peers); goto send; toobig_allowedips: if (nest_curr_allowed_ip) nla_nest_cancel(msg, nest_curr_allowed_ip); if (nest_allowed_ips) - _nla_nest_end(msg, nest_allowed_ips); - _nla_nest_end(msg, nest_curr_peer); - _nla_nest_end(msg, nest_peers); + NLA_NEST_END(msg, nest_allowed_ips); + NLA_NEST_END(msg, nest_curr_peer); + NLA_NEST_END(msg, nest_peers); goto send; toobig_peers: if (nest_curr_peer) nla_nest_cancel(msg, nest_curr_peer); - _nla_nest_end(msg, nest_peers); + NLA_NEST_END(msg, nest_peers); goto send; send: @@ -2844,8 +3073,6 @@ send: nla_put_failure: g_return_val_if_reached(-NME_BUG); - -#undef _nla_nest_end } static int @@ -2859,12 +3086,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, @@ -2882,14 +3109,14 @@ link_wireguard_change(NMPlatform *platform, } for (i = 0; i < msgs->len; i++) { - r = nl_send_auto(priv->genl, msgs->pdata[i]); + r = nl_send_auto(priv->sk_genl_sync, msgs->pdata[i]); if (r < 0) { _LOGW("wireguard: set-device, send netlink message #%u failed: %s", i, nm_strerror(r)); return r; } do { - r = nl_recvmsgs(priv->genl, NULL); + r = nl_recvmsgs(priv->sk_genl_sync, NULL); } while (r == -EAGAIN); if (r < 0) { _LOGW("wireguard: set-device, message #%u was rejected: %s", i, nm_strerror(r)); @@ -2925,10 +3152,10 @@ _nmp_link_address_set(NMPLinkAddress *dst, const struct nlattr *nla) /* Copied and heavily modified from libnl3's link_msg_parser(). */ static NMPObject * -_new_from_nl_link(NMPlatform *platform, - const NMPCache *cache, - struct nlmsghdr *nlh, - gboolean id_only) +_new_from_nl_link(NMPlatform *platform, + const NMPCache *cache, + const struct nlmsghdr *nlh, + gboolean id_only) { static const struct nla_policy policy[] = { [IFLA_IFNAME] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, @@ -3117,6 +3344,9 @@ _new_from_nl_link(NMPlatform *platform, case NM_LINK_TYPE_BRIDGE: lnk_data = _parse_lnk_bridge(nl_info_kind, nl_info_data); break; + case NM_LINK_TYPE_BOND: + lnk_data = _parse_lnk_bond(nl_info_kind, nl_info_data); + break; case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_GRETAP: lnk_data = _parse_lnk_gre(nl_info_kind, nl_info_data); @@ -3220,16 +3450,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, - _genl_sock(NM_LINUX_PLATFORM(platform)), - 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, - _genl_sock(NM_LINUX_PLATFORM(platform)), - 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(); @@ -3238,28 +3470,17 @@ _new_from_nl_link(NMPlatform *platform, if (obj->link.type == NM_LINK_TYPE_WIREGUARD) { const NMPObject *lnk_data_new = NULL; - struct nl_sock *genl = NM_LINUX_PLATFORM_GET_PRIVATE(platform)->genl; + struct nl_sock *genl = NM_LINUX_PLATFORM_GET_PRIVATE(platform)->sk_genl_sync; /* The WireGuard kernel module does not yet send link update * 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)) @@ -3276,7 +3497,7 @@ _new_from_nl_link(NMPlatform *platform, /* Copied and heavily modified from libnl3's addr_msg_parser(). */ static NMPObject * -_new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_addr(const struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [IFA_LABEL] = {.type = NLA_STRING, .maxlen = IFNAMSIZ}, @@ -3391,7 +3612,7 @@ _new_from_nl_addr(struct nlmsghdr *nlh, gboolean id_only) /* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ static NMPObject * -_new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter) +_new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse_nlmsg_iter) { static const struct nla_policy policy[] = { [RTA_TABLE] = {.type = NLA_U32}, @@ -3420,13 +3641,15 @@ _new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse .found = FALSE, .has_more = FALSE, }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 lock = 0; + guint32 mss; + guint32 window = 0; + guint32 cwnd = 0; + guint32 initcwnd = 0; + guint32 initrwnd = 0; + guint32 mtu = 0; + guint32 rto_min = 0; + guint32 lock = 0; + gboolean quickack = FALSE; nm_assert((parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop > 0) || (!parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop == 0)); @@ -3604,6 +3827,8 @@ rta_multipath_done: [RTAX_INITCWND] = {.type = NLA_U32}, [RTAX_INITRWND] = {.type = NLA_U32}, [RTAX_MTU] = {.type = NLA_U32}, + [RTAX_QUICKACK] = {.type = NLA_U32}, + [RTAX_RTO_MIN] = {.type = NLA_U32}, }; struct nlattr *mtb[G_N_ELEMENTS(rtax_policy)]; @@ -3624,6 +3849,10 @@ rta_multipath_done: initrwnd = nla_get_u32(mtb[RTAX_INITRWND]); if (mtb[RTAX_MTU]) mtu = nla_get_u32(mtb[RTAX_MTU]); + if (mtb[RTAX_RTO_MIN]) + rto_min = nla_get_u32(mtb[RTAX_RTO_MIN]); + if (mtb[RTAX_QUICKACK]) + quickack = !!nla_get_u32(mtb[RTAX_QUICKACK]); } /*****************************************************************/ @@ -3674,12 +3903,15 @@ rta_multipath_done: obj->ip_route.cwnd = cwnd; obj->ip_route.initcwnd = initcwnd; obj->ip_route.initrwnd = initrwnd; + obj->ip_route.rto_min = rto_min; + obj->ip_route.quickack = quickack; obj->ip_route.mtu = mtu; obj->ip_route.lock_window = NM_FLAGS_HAS(lock, 1 << RTAX_WINDOW); obj->ip_route.lock_cwnd = NM_FLAGS_HAS(lock, 1 << RTAX_CWND); obj->ip_route.lock_initcwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITCWND); obj->ip_route.lock_initrwnd = NM_FLAGS_HAS(lock, 1 << RTAX_INITRWND); obj->ip_route.lock_mtu = NM_FLAGS_HAS(lock, 1 << RTAX_MTU); + obj->ip_route.lock_mss = NM_FLAGS_HAS(lock, 1 << RTAX_ADVMSS); if (!IS_IPv4) { if (tb[RTA_PREF]) @@ -3699,7 +3931,7 @@ rta_multipath_done: } static NMPObject * -_new_from_nl_routing_rule(struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_routing_rule(const struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [FRA_UNSPEC] = {}, @@ -3975,7 +4207,7 @@ psched_tick_to_time(NMPlatform *platform, guint32 tick) } static NMPObject * -_new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_qdisc(NMPlatform *platform, const struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = {.type = NLA_STRING}, @@ -4088,7 +4320,7 @@ _new_from_nl_qdisc(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) } static NMPObject * -_new_from_nl_tfilter(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_only) +_new_from_nl_tfilter(NMPlatform *platform, const struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = {.type = NLA_STRING}, @@ -4133,18 +4365,17 @@ _new_from_nl_tfilter(NMPlatform *platform, struct nlmsghdr *nlh, gboolean id_onl * Returns: %NULL or a newly created NMPObject instance. **/ static NMPObject * -nmp_object_new_from_nl(NMPlatform *platform, - const NMPCache *cache, - struct nl_msg *msg, - gboolean id_only, - ParseNlmsgIter *parse_nlmsg_iter) +nmp_object_new_from_nl(NMPlatform *platform, + const NMPCache *cache, + const struct nl_msg_lite *msg, + gboolean id_only, + ParseNlmsgIter *parse_nlmsg_iter) { - struct nlmsghdr *msghdr; + const struct nlmsghdr *msghdr; - if (nlmsg_get_proto(msg) != NETLINK_ROUTE) - return NULL; + nm_assert(msg->nm_protocol == NETLINK_ROUTE); - msghdr = nlmsg_hdr(msg); + msghdr = msg->nm_nlh; switch (msghdr->nlmsg_type) { case RTM_NEWLINK: @@ -4268,6 +4499,86 @@ _nl_msg_new_link_set_linkinfo(struct nl_msg *msg, NMLinkType link_type, gconstpo NLA_PUT_U64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, props->mcast_startup_query_interval); break; } + case NM_LINK_TYPE_BOND: + { + const NMPlatformLnkBond *props = extra_data; + struct nlattr *targets; + int i = 0; + + nm_assert(extra_data); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (props->arp_ip_targets_num > 0) { + targets = nla_nest_start(msg, IFLA_BOND_ARP_IP_TARGET); + if (!targets) + goto nla_put_failure; + + for (i = 0; i < props->arp_ip_targets_num; i++) + NLA_PUT_U32(msg, i, props->arp_ip_target[i]); + + nla_nest_end(msg, targets); + } + + if (props->arp_all_targets_has) + NLA_PUT_U32(msg, IFLA_BOND_ARP_ALL_TARGETS, props->arp_all_targets); + if (props->arp_interval) + NLA_PUT_U32(msg, IFLA_BOND_ARP_INTERVAL, props->arp_interval); + if (props->arp_validate) + NLA_PUT_U32(msg, IFLA_BOND_ARP_VALIDATE, props->arp_validate); + if (props->downdelay_has) + NLA_PUT_U32(msg, IFLA_BOND_DOWNDELAY, props->downdelay); + if (props->lp_interval_has) + NLA_PUT_U32(msg, IFLA_BOND_LP_INTERVAL, props->lp_interval); + if (props->miimon_has) + NLA_PUT_U32(msg, IFLA_BOND_MIIMON, props->miimon); + if (props->min_links) + NLA_PUT_U32(msg, IFLA_BOND_MIN_LINKS, props->min_links); + if (props->packets_per_port) + NLA_PUT_U32(msg, IFLA_BOND_PACKETS_PER_SLAVE, props->packets_per_port); + if (props->peer_notif_delay_has) + NLA_PUT_U32(msg, IFLA_BOND_PEER_NOTIF_DELAY, props->peer_notif_delay); + if (props->primary) + NLA_PUT_U32(msg, IFLA_BOND_PRIMARY, props->primary); + if (props->resend_igmp_has) + NLA_PUT_U32(msg, IFLA_BOND_RESEND_IGMP, props->resend_igmp); + if (props->updelay_has) + NLA_PUT_U32(msg, IFLA_BOND_UPDELAY, props->updelay); + if (props->ad_actor_sys_prio) + NLA_PUT_U16(msg, IFLA_BOND_AD_ACTOR_SYS_PRIO, props->ad_actor_sys_prio); + if (props->ad_user_port_key) + NLA_PUT_U16(msg, IFLA_BOND_AD_USER_PORT_KEY, props->ad_user_port_key); + if (!nm_ether_addr_equal(&props->ad_actor_system, &nm_ether_addr_zero)) + NLA_PUT(msg, + IFLA_BOND_AD_ACTOR_SYSTEM, + sizeof(props->ad_actor_system), + &props->ad_actor_system); + if (props->ad_select) + NLA_PUT_U8(msg, IFLA_BOND_AD_SELECT, props->ad_select); + + NLA_PUT_U8(msg, IFLA_BOND_ALL_SLAVES_ACTIVE, props->all_ports_active); + + if (props->fail_over_mac) + NLA_PUT_U8(msg, IFLA_BOND_FAIL_OVER_MAC, props->fail_over_mac); + if (props->lacp_rate) + NLA_PUT_U8(msg, IFLA_BOND_AD_LACP_RATE, props->lacp_rate); + if (props->num_grat_arp) + NLA_PUT_U8(msg, IFLA_BOND_NUM_PEER_NOTIF, props->num_grat_arp); + + NLA_PUT_U8(msg, IFLA_BOND_MODE, props->mode); + + if (props->primary_reselect) + NLA_PUT_U8(msg, IFLA_BOND_PRIMARY_RESELECT, props->primary_reselect); + if (props->xmit_hash_policy) + NLA_PUT_U8(msg, IFLA_BOND_XMIT_HASH_POLICY, props->xmit_hash_policy); + if (props->tlb_dynamic_lb_has) + NLA_PUT_U8(msg, IFLA_BOND_TLB_DYNAMIC_LB, !!props->tlb_dynamic_lb); + + NLA_PUT_U8(msg, IFLA_BOND_USE_CARRIER, !!props->use_carrier); + + break; + } case NM_LINK_TYPE_VLAN: { const NMPlatformLnkVlan *props = extra_data; @@ -4655,8 +4966,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, @@ -4688,22 +4999,22 @@ 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, guint8 plen, gconstpointer peer_address, guint32 flags, - int scope, + guint8 scope, guint32 lifetime, guint32 preferred, in_addr_t ip4_broadcast_address, @@ -4715,6 +5026,7 @@ _nl_msg_new_address(int nlmsg_type, .ifa_index = ifindex, .ifa_prefixlen = plen, .ifa_flags = flags, + .ifa_scope = scope, }; gsize addr_len; @@ -4723,15 +5035,6 @@ _nl_msg_new_address(int nlmsg_type, msg = nlmsg_alloc_simple(nlmsg_type, nlmsg_flags); - if (scope == -1) { - /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ - if (family == AF_INET && address && *((char *) address) == 127) - scope = RT_SCOPE_HOST; - else - scope = RT_SCOPE_UNIVERSE; - } - am.ifa_scope = scope, - addr_len = family == AF_INET ? sizeof(in_addr_t) : sizeof(struct in6_addr); if (nlmsg_append_struct(msg, &am) < 0) @@ -4783,7 +5086,8 @@ ip_route_get_lock_flag(const NMPlatformIPRoute *route) | (((guint32) route->lock_cwnd) << RTAX_CWND) | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); + | (((guint32) route->lock_mtu) << RTAX_MTU) + | (((guint32) route->lock_mss) << RTAX_ADVMSS); } static gboolean @@ -4809,7 +5113,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); @@ -4836,7 +5140,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; @@ -4871,7 +5175,8 @@ _nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) } if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd - || obj->ip_route.initrwnd || obj->ip_route.mtu || lock) { + || obj->ip_route.initrwnd || obj->ip_route.mtu || obj->ip_route.quickack + || obj->ip_route.rto_min || lock) { struct nlattr *metrics; metrics = nla_nest_start(msg, RTA_METRICS); @@ -4890,6 +5195,10 @@ _nl_msg_new_route(int nlmsg_type, guint16 nlmsgflags, const NMPObject *obj) NLA_PUT_U32(msg, RTAX_INITRWND, obj->ip_route.initrwnd); if (obj->ip_route.mtu) NLA_PUT_U32(msg, RTAX_MTU, obj->ip_route.mtu); + if (obj->ip_route.rto_min) + NLA_PUT_U32(msg, RTAX_RTO_MIN, obj->ip_route.rto_min); + if (obj->ip_route.quickack) + NLA_PUT_U32(msg, RTAX_QUICKACK, obj->ip_route.quickack); if (lock) NLA_PUT_U32(msg, RTAX_LOCK, lock); @@ -4915,7 +5224,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); @@ -5028,7 +5339,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; @@ -5115,7 +5426,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; @@ -5154,7 +5465,7 @@ _nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tf if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { const NMPlatformActionSimple *simple = &action->simple; struct tc_defact sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5168,7 +5479,7 @@ _nl_msg_new_tfilter(int nlmsg_type, int nlmsg_flags, const NMPlatformTfilter *tf } else if (nm_streq(action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { const NMPlatformActionMirred *mirred = &action->mirred; struct tc_mirred sel = { - 0, + 0, }; if (!(act_options = nla_nest_start(msg, TCA_ACT_OPTIONS))) @@ -5204,14 +5515,6 @@ nla_put_failure: /*****************************************************************************/ -static struct nl_sock * -_genl_sock(NMLinuxPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - - return priv->genl; -} - #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ G_STMT_START \ { \ @@ -5781,7 +6084,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); } /*****************************************************************************/ @@ -5789,27 +6095,34 @@ process_events(NMPlatform *platform) static const RefreshAllInfo * refresh_all_type_get_info(RefreshAllType refresh_all_type) { +#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, \ + } +#define R_ROUTE(...) R(NMP_NETLINK_ROUTE, __VA_ARGS__) +#define R_GENERIC(...) R(NMP_NETLINK_GENERIC, __VA_ARGS__) static const RefreshAllInfo infos[] = { -#define R(_refresh_all_type, _obj_type, _addr_family) \ - [_refresh_all_type] = { \ - .obj_type = _obj_type, \ - .addr_family = _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 + 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]; } @@ -5819,19 +6132,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 @@ -5853,25 +6171,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; @@ -5895,11 +6213,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); } @@ -5915,28 +6233,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, @@ -5947,6 +6279,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)); @@ -5954,7 +6287,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) { @@ -6005,8 +6339,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; @@ -6016,25 +6350,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) { @@ -6055,15 +6390,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; @@ -6071,24 +6407,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; @@ -6099,9 +6436,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); @@ -6111,9 +6449,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); } /*****************************************************************************/ @@ -6148,15 +6492,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 @@ -6164,6 +6508,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; @@ -6190,28 +6536,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; + 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; - flags = priv->delayed_action.flags & DELAYED_ACTION_TYPE_REFRESH_ALL; + if (_LOGt_ENABLED()) { + FOR_EACH_DELAYED_ACTION (iflags, FLAGS) + _LOGt_delayed_action(iflags, NULL, "handle"); + } - priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_ALL; + 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) + 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; } @@ -6235,18 +6599,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; @@ -6254,8 +6624,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--; @@ -6290,14 +6658,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; } @@ -6310,12 +6685,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, @@ -6327,7 +6727,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); } /*****************************************************************************/ @@ -6379,6 +6782,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; @@ -6462,15 +6868,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); } } { @@ -6518,8 +6927,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); } } @@ -6627,8 +7036,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; @@ -6640,12 +7049,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)); } /** @@ -6672,7 +7083,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; { @@ -6689,12 +7100,12 @@ _nl_send_nlmsghdr(NMPlatform *platform, int try_count; if (!nlhdr->nlmsg_pid) - nlhdr->nlmsg_pid = nl_socket_get_local_port(priv->nlh); + nlhdr->nlmsg_pid = nl_socket_get_local_port(priv->sk_rtnl); nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); try_count = 0; again: - errsv = sendmsg(nl_socket_get_fd(priv->nlh), &msg, 0); + errsv = sendmsg(nl_socket_get_fd(priv->sk_rtnl), &msg, 0); if (errsv < 0) { errsv = errno; if (errsv == EINTR && try_count++ < 100) @@ -6706,32 +7117,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; @@ -6739,24 +7142,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->nlh, 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) { @@ -6776,16 +7195,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, @@ -6801,11 +7220,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; @@ -6855,6 +7274,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) { @@ -6862,29 +7301,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); @@ -6905,7 +7351,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)); @@ -6918,23 +7364,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; @@ -6946,102 +7399,109 @@ 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; + + nm_assert(priv->delayed_action.list_wait_for_response_x[netlink_protocol]->len > 0); - 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); + 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. + 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; +#else + (void) 0; #endif } static void -event_valid_msg(NMPlatform *platform, struct nl_msg *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; nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; - struct nlmsghdr *msghdr; + const struct nlmsghdr *msghdr; char buf_nlmsghdr[400]; gboolean is_del = FALSE; gboolean is_dump = FALSE; NMPCache *cache = nm_platform_get_cache(platform); ParseNlmsgIter parse_nlmsg_iter; - if (!handle_events) - return; - - msghdr = nlmsg_hdr(msg); + msghdr = msg->nm_nlh; if (NM_IN_SET(msghdr->nlmsg_type, RTM_DELLINK, @@ -7062,7 +7522,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events obj = nmp_object_new_from_nl(platform, cache, msg, is_del, &parse_nlmsg_iter); if (!obj) { _LOGT("event-notification: %s: ignore", - nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); + nl_nlmsghdr_to_str(NETLINK_ROUTE, 0, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); return; } @@ -7080,7 +7540,7 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events } _LOGT("event-notification: %s%s: %s", - nl_nlmsghdr_to_str(msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)), + nl_nlmsghdr_to_str(NETLINK_ROUTE, 0, msghdr, buf_nlmsghdr, sizeof(buf_nlmsghdr)), is_dump ? ", in-dump" : "", nmp_object_to_string(obj, is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, @@ -7119,20 +7579,18 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events 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) { nm_assert(!*data->response.out_route_get); - if (data->seq_number == nlmsg_hdr(msg)->nlmsg_seq) { + if (data->seq_number == msg->nm_nlh->nlmsg_seq) { *data->response.out_route_get = nmp_object_clone(obj, FALSE); data->response.out_route_get = NULL; break; @@ -7269,14 +7727,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, @@ -7287,7 +7740,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); @@ -7323,14 +7776,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, @@ -7340,7 +7788,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); @@ -7379,14 +7827,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, @@ -7396,7 +7839,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); @@ -7470,12 +7913,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 = @@ -7488,7 +7926,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); @@ -7760,7 +8198,7 @@ link_set_address(NMPlatform *platform, int ifindex, gconstpointer address, size_ nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const ChangeLinkData d = { .set_address = - { + { .address = address, .length = length, }, @@ -8596,11 +9034,11 @@ link_can_assume(NMPlatform *platform, int ifindex) if (link->link.master > 0) return TRUE; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); if (nmp_cache_lookup(cache, &lookup)) return TRUE; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex); nmp_cache_iter_for_each (&iter, nmp_cache_lookup(cache, &lookup), &o) { nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP6_ADDRESS); if (!IN6_IS_ADDR_LINKLOCAL(&o->ip6_address.address)) @@ -8750,8 +9188,7 @@ ip4_address_add(NMPlatform *platform, plen, &peer_addr, flags, - nm_utils_ip4_address_is_link_local(addr) ? RT_SCOPE_LINK - : RT_SCOPE_UNIVERSE, + nm_platform_ip4_address_get_scope(addr), lifetime, preferred, broadcast_address, @@ -8897,6 +9334,9 @@ object_delete(NMPlatform *platform, const NMPObject *obj) case NMP_OBJECT_TYPE_TFILTER: nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj)); break; + case NMP_OBJECT_TYPE_MPTCP_ADDR: + return (nm_platform_mptcp_addr_update(platform, FALSE, NMP_OBJECT_CAST_MPTCP_ADDR(obj)) + >= 0); default: break; } @@ -8967,7 +9407,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. */ @@ -9003,20 +9443,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); @@ -9047,20 +9482,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); @@ -9076,7 +9506,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; @@ -9089,7 +9523,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; @@ -9101,19 +9535,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)", @@ -9123,7 +9552,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); @@ -9163,20 +9592,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); @@ -9199,93 +9623,210 @@ tfilter_delete(NMPlatform *platform, int ifindex, guint32 parent, gboolean log_e /*****************************************************************************/ static gboolean -event_handler(int fd, GIOCondition io_condition, gpointer user_data) +_genl_family_id_update(NMPlatform *platform, NMPGenlFamilyType family_type, guint16 family_id) { - delayed_action_handle_all(NM_PLATFORM(user_data), TRUE); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + + if (priv->genl_family_data[family_type].family_id == family_id) + return FALSE; + + if (family_id != 0) { + _LOGD("genl:ctrl: new family-id for %s: 0x%x", + nmp_genl_family_infos[family_type].name, + family_id); + } else + _LOGD("genl:ctrl: del family-id for %s", nmp_genl_family_infos[family_type].name); + priv->genl_family_data[family_type].family_id = family_id; return TRUE; } +static void +_genl_handle_msg_ctrl(NMPlatform *platform, const struct nlmsghdr *hdr) +{ + 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]); + + _genl_family_id_update(platform, family_type, 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); +} + /*****************************************************************************/ -/* copied from libnl3's recvmsgs() */ static int -event_handler_recvmsgs(NMPlatform *platform, gboolean handle_events) +_netlink_recv(NMPlatform *platform, + struct nl_sock *sk, + struct sockaddr_nl *nla, + struct ucred *out_creds, + gboolean *out_creds_has, + guint32 *out_pktinfo_group, + gboolean *out_pktinfo_has) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); - struct nl_sock *sk = priv->nlh; + unsigned char *buf = NULL; int n; - int err = 0; - gboolean multipart = 0; - gboolean interrupted = FALSE; - struct nlmsghdr *hdr; - WaitForNlResponseResult seq_result; - struct sockaddr_nl nla; - struct ucred creds; - gboolean creds_has; - unsigned char *buf; -continue_reading: - buf = NULL; + nm_assert(nla); + nm_assert(out_creds); + nm_assert(out_creds_has); + + /* 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, priv->netlink_recv_buf.len, - &nla, + nla, &buf, - &creds, - &creds_has); + out_creds, + out_creds_has, + 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)); - if (n <= 0) { - if (n == -NME_NL_MSG_TRUNC) { - /* the message receive buffer was too small. We lost one message, which - * is unfortunate. Try to double the buffer size for the next time. */ - priv->netlink_recv_buf.len *= 2; - priv->netlink_recv_buf.buf = - g_realloc(priv->netlink_recv_buf.buf, priv->netlink_recv_buf.len); - _LOGT("netlink: recvmsg: increase message buffer size for recvmsg() to %zu bytes", - priv->netlink_recv_buf.len); - if (!handle_events) - goto continue_reading; - } - - return n; + if (n == -NME_NL_MSG_TRUNC) { + /* the message receive buffer was too small. We lost one message, which + * is unfortunate. Try to double the buffer size for the next time. */ + priv->netlink_recv_buf.len *= 2; + priv->netlink_recv_buf.buf = + g_realloc(priv->netlink_recv_buf.buf, priv->netlink_recv_buf.len); + _LOGT("netlink: recvmsg: increase message buffer size for recvmsg() to %zu bytes", + priv->netlink_recv_buf.len); } - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok(hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - gboolean abort_parsing = FALSE; - gboolean process_valid_msg = FALSE; - guint32 seq_number; - char buf_nlmsghdr[400]; - const char *extack_msg = NULL; + return n; +} - msg = nlmsg_alloc_convert(hdr); +/*****************************************************************************/ - nlmsg_set_proto(msg, NETLINK_ROUTE); - nlmsg_set_src(msg, &nla); +static gboolean +_nl_event_handler(NMPlatform *platform, DelayedActionType action_type) +{ + delayed_action_schedule(platform, action_type, NULL); + delayed_action_handle_all(platform); + return TRUE; +} - if (!creds_has || creds.pid) { - if (!creds_has) - _LOGT("netlink: recvmsg: received message without credentials"); - else - _LOGT("netlink: recvmsg: received non-kernel message (pid %d)", creds.pid); - err = 0; - goto stop; - } +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, + NMPNetlinkProtocol netlink_protocol, + gboolean handle_events) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + int n; + int retval = 0; + gboolean multipart = 0; + gboolean interrupted = FALSE; + struct nlmsghdr *hdr; + struct sockaddr_nl nla; + struct ucred creds; + gboolean creds_has; + 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, + 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; + return n; + } + + if (!creds_has || creds.pid) { + if (!creds_has) + _LOGT("%s: recvmsg: received message without credentials", log_prefix); + else + _LOGT("%s: recvmsg: received non-kernel message (pid %d)", log_prefix, creds.pid); + goto stop; + } - _LOGt("netlink: recvmsg: new message %s", - nl_nlmsghdr_to_str(hdr, buf_nlmsghdr, sizeof(buf_nlmsghdr))); + hdr = (struct nlmsghdr *) priv->netlink_recv_buf.buf; + while (nlmsg_ok(hdr, n)) { + WaitForNlResponseResult seq_result; + gboolean process_valid_msg = FALSE; + char buf_nlmsghdr[400]; + const char *extack_msg = NULL; + const struct nl_msg_lite msg = { + .nm_protocol = nmp_netlink_protocol_info(netlink_protocol)->netlink_protocol, + .nm_src = &nla, + .nm_creds = &creds, + .nm_size = NLMSG_ALIGN(hdr->nlmsg_len), + .nm_nlh = hdr, + }; + const guint32 seq_number = msg.nm_nlh->nlmsg_seq; + + nm_assert((((uintptr_t) (const void *) msg.nm_nlh) % NLMSG_ALIGNTO) == 0); - nlmsg_set_creds(msg, &creds); + _LOGt("%s: recvmsg: new message %s", + log_prefix, + nl_nlmsghdr_to_str(nmp_netlink_protocol_info(netlink_protocol)->netlink_protocol, + pktinfo_group, + msg.nm_nlh, + buf_nlmsghdr, + sizeof(buf_nlmsghdr))); - if (hdr->nlmsg_flags & NLM_F_MULTI) + if (msg.nm_nlh->nlmsg_flags & NLM_F_MULTI) multipart = TRUE; - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { + if (msg.nm_nlh->nlmsg_flags & NLM_F_DUMP_INTR) { /* * We have to continue reading to clear * all messages until a NLMSG_DONE is @@ -9294,105 +9835,75 @@ continue_reading: interrupted = TRUE; } - /* Other side wishes to see an ack for this message */ - if (hdr->nlmsg_flags & NLM_F_ACK) { - /* FIXME: implement */ + if (msg.nm_nlh->nlmsg_flags & NLM_F_ACK) { + /* TODO: Other side wishes to see an ack for this message */ } seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; - if (hdr->nlmsg_type == NLMSG_DONE) { + if (msg.nm_nlh->nlmsg_type == NLMSG_DONE) { /* messages terminates a multipart message, this is * usually the end of a message and therefore we slip * out of the loop by default. the user may overrule * this action by skipping this packet. */ multipart = FALSE; seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; - } else if (hdr->nlmsg_type == NLMSG_NOOP) { + } else if (msg.nm_nlh->nlmsg_type == NLMSG_NOOP) { /* Message to be ignored, the default action is to * skip this message if no callback is specified. The * user may overrule this action by returning * NL_PROCEED. */ - } else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + } else if (msg.nm_nlh->nlmsg_type == NLMSG_OVERRUN) { /* Data got lost, report back to user. The default action is to * quit parsing. The user may overrule this action by returning * NL_SKIP or NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_OVERFLOW; - abort_parsing = TRUE; - } else if (hdr->nlmsg_type == NLMSG_ERROR) { - /* Message carries a nlmsgerr */ - struct nlmsgerr *e = nlmsg_data(hdr); - - if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - err = -NME_NL_MSG_TRUNC; - abort_parsing = TRUE; - } else if (e->error) { - int errsv = nm_errno_native(e->error); - - if (NM_FLAGS_HAS(hdr->nlmsg_flags, NLM_F_ACK_TLVS) - && hdr->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) { - static const struct nla_policy policy[] = { - [NLMSGERR_ATTR_MSG] = {.type = NLA_STRING}, - [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32}, - }; - struct nlattr *tb[G_N_ELEMENTS(policy)]; - struct nlattr *tlvs; - - tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - - NLMSG_HDRLEN); - if (nla_parse_arr(tb, - tlvs, - hdr->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, - policy) - >= 0) { - if (tb[NLMSGERR_ATTR_MSG]) - extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); - } - } + retval = -NME_NL_MSG_OVERFLOW; + } else if (msg.nm_nlh->nlmsg_type == NLMSG_ERROR) { + int errsv; - /* Error message reported back from kernel. */ - _LOGD("netlink: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", - nm_strerror_native(errsv), + errsv = nlmsg_parse_error(msg.nm_nlh, &extack_msg); + if (errsv == 0) + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + else { + _LOGD("%s: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d", + log_prefix, + nm_strerror(errsv), errsv, NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""), - nlmsg_hdr(msg)->nlmsg_seq); - seq_result = -NM_ERRNO_NATIVE(errsv); - } else - seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + msg.nm_nlh->nlmsg_seq); + seq_result = errsv; + } } else process_valid_msg = TRUE; - seq_number = nlmsg_hdr(msg)->nlmsg_seq; - /* 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 event_valid_msg(), + * 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); + event_seq_check_refresh_all(platform, netlink_protocol, seq_number); if (process_valid_msg) { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - - event_valid_msg(platform, msg, handle_events); - + if (handle_events) { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + if (netlink_protocol == NMP_NETLINK_ROUTE) { + _rtnl_handle_msg(platform, &msg); + } else { + _genl_handle_msg(platform, pktinfo_group, &msg); + } + } seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } - event_seq_check(platform, seq_number, seq_result, extack_msg); + event_seq_check(platform, netlink_protocol, seq_number, seq_result, extack_msg); - if (abort_parsing) + if (retval != 0) goto stop; - err = 0; hdr = nlmsg_next(hdr, &n); } @@ -9400,6 +9911,7 @@ continue_reading: /* Multipart message not yet complete, continue reading */ goto continue_reading; } + stop: if (!handle_events) { /* when we don't handle events, we want to drain all messages from the socket @@ -9410,13 +9922,15 @@ stop: if (interrupted) return -NME_NL_DUMP_INTR; - return err; + return retval; } /*****************************************************************************/ 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); @@ -9430,8 +9944,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; } @@ -9440,20 +9957,24 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) for (;;) { int nle; - nle = event_handler_recvmsgs(platform, 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: @@ -9465,24 +9986,16 @@ event_handler_read_netlink(NMPlatform *platform, gboolean wait_for_acks) } _reason; })); - event_handler_recvmsgs(platform, 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; @@ -9493,17 +10006,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); @@ -9521,7 +10039,7 @@ after_read: timeout_msec); memset(&pfd, 0, sizeof(pfd)); - pfd.fd = nl_socket_get_fd(priv->nlh); + pfd.fd = nl_socket_get_fd(priv->sk_rtnl); pfd.events = POLLIN; r = poll(&pfd, 1, timeout_msec); @@ -9539,6 +10057,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; } @@ -9549,6 +10068,337 @@ after_read: /*****************************************************************************/ +static guint16 +genl_get_family_id(NMPlatform *platform, NMPGenlFamilyType family_type) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + int family_id; + + 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) + goto out; + + /* Unknown family ID... usually we expect to start using the protocol. + * Let's try harder and fetch the ID synchronously. + * + * Note that we might also be called back during delayed_action_handle_all(), + * when we add a WireGuard link, the module gets autoloaded, and we didn't + * yet process the genl notification about the new family. Let's not call + * delayed_action_handle_all() again, because that might emit various + * signals. */ + + family_id = genl_ctrl_resolve(priv->sk_genl_sync, nmp_genl_family_infos[family_type].name); + if (family_id < 0) + family_id = 0; + + /* We cache the family ID and update it via genl notifications. + * Here we bypass the order of that, and update the cached value + * directly. */ + _genl_family_id_update(platform, family_type, family_id); + +out: + return priv->genl_family_data[family_type].family_id; +} + +/*****************************************************************************/ + +static const NMPObject * +_mptcp_addrs_dump_parse_addr(struct nlattr *attr) +{ + static const struct nla_policy policy[] = { + [MPTCP_PM_ADDR_ATTR_FAMILY] = {.type = NLA_U16}, + [MPTCP_PM_ADDR_ATTR_ID] = {.type = NLA_U8}, + [MPTCP_PM_ADDR_ATTR_ADDR4] = {.minlen = sizeof(in_addr_t)}, + [MPTCP_PM_ADDR_ATTR_ADDR6] = {.minlen = sizeof(struct in6_addr)}, + [MPTCP_PM_ADDR_ATTR_PORT] = {.type = NLA_U16}, + [MPTCP_PM_ADDR_ATTR_FLAGS] = {.type = NLA_U32}, + [MPTCP_PM_ADDR_ATTR_IF_IDX] = {.type = NLA_S32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPlatformMptcpAddr *mptcp_addr; + int addr_family; + int addr_attr; + + if (nla_parse_nested_arr(tb, attr, policy) < 0) + return NULL; + + if (!tb[MPTCP_PM_ADDR_ATTR_ID]) + return NULL; + + obj = nmp_object_new(NMP_OBJECT_TYPE_MPTCP_ADDR, NULL); + mptcp_addr = &obj->mptcp_addr; + + mptcp_addr->id = nla_get_u8(tb[MPTCP_PM_ADDR_ATTR_ID]); + + if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) { + /* If we don't have the family. Only create a stub object containing + * the ID. */ + goto out; + } + + addr_family = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_FAMILY]); + + if (addr_family == AF_INET) + addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4; + else if (addr_family == AF_INET6) + addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6; + else + goto out; + + if (!tb[addr_attr]) + goto out; + + mptcp_addr->addr_family = addr_family; + memcpy(&mptcp_addr->addr, nla_data(tb[addr_attr]), nm_utils_addr_family_to_size(addr_family)); + + if (tb[MPTCP_PM_ADDR_ATTR_PORT]) + mptcp_addr->port = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT]); + + if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX]) + mptcp_addr->ifindex = nla_get_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]); + + if (tb[MPTCP_PM_ADDR_ATTR_FLAGS]) + mptcp_addr->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]); + +out: + + return g_steal_pointer(&obj); +} + +typedef struct { + GPtrArray *addrs; +} FetchMptcpAddrParseData; + +static int +_mptcp_addrs_dump_parse_cb(const struct nl_msg *msg, void *arg) +{ + static const struct nla_policy policy[] = { + [MPTCP_PM_ATTR_ADDR] = {.type = NLA_NESTED}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + FetchMptcpAddrParseData *parse_data = arg; + + if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0) + return NL_SKIP; + + if (tb[MPTCP_PM_ATTR_ADDR]) { + const NMPObject *obj; + + obj = _mptcp_addrs_dump_parse_addr(tb[MPTCP_PM_ATTR_ADDR]); + if (obj) + g_ptr_array_add(parse_data->addrs, (gpointer) obj); + } + + return NL_OK; +} + +#define EXTACK_MSG_BUFSIZE 200 + +static int +_mptcp_addr_update_err_cb(const struct sockaddr_nl *nla, const struct nlmsgerr *nlerr, void *arg) +{ + char *out_extack_msg = arg; + const char *extack_msg; + int errsv; + + errsv = nlmsg_parse_error(nlmsg_undata(nlerr), &extack_msg); + if (extack_msg) + g_strlcpy(out_extack_msg, extack_msg, EXTACK_MSG_BUFSIZE); + return errsv; +} + +static int +mptcp_addr_update(NMPlatform *platform, NMOptionBool add, const NMPlatformMptcpAddr *addr) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct nlattr *nla_nest; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + guint16 genl_family_id; + guint8 cmd_num; + const char *cmd_str; + int nle; + char extack_msg[EXTACK_MSG_BUFSIZE]; + const struct nl_cb cb = { + .err_cb = _mptcp_addr_update_err_cb, + .err_arg = extack_msg, + }; + + extack_msg[0] = '\0'; + + if (add == NM_OPTION_BOOL_DEFAULT) { + cmd_num = MPTCP_PM_CMD_SET_FLAGS; + cmd_str = "update"; + } else if (add) { + cmd_num = MPTCP_PM_CMD_ADD_ADDR; + cmd_str = "add"; + } else { + cmd_num = MPTCP_PM_CMD_DEL_ADDR; + cmd_str = "delete"; + } + + nm_assert_addr_family_or_unspec(addr->addr_family); + nm_assert(cmd_num != MPTCP_PM_CMD_ADD_ADDR + || (addr->port == 0 && addr->addr_family != AF_UNSPEC)); + nm_assert(cmd_num != MPTCP_PM_CMD_SET_FLAGS || (addr->id != 0 && addr->port == 0)); + nm_assert(cmd_num != MPTCP_PM_CMD_DEL_ADDR || addr->id != 0); + + genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM); + if (genl_family_id == 0) { + _LOGT("mptcp: %s address %s fails because %s generic netlink family is unknown", + cmd_str, + nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)), + nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name); + return -ENOSYS; + } + + _LOGT("mptcp: %s address %s", + cmd_str, + nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf))); + + nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN) + 200); + + if (!genlmsg_put(nlmsg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + genl_family_id, + 0, + NLM_F_REQUEST, + cmd_num, + MPTCP_PM_VER)) + goto nla_put_failure; + + nla_nest = nla_nest_start(nlmsg, MPTCP_PM_ATTR_ADDR | NLA_F_NESTED); + if (!nla_nest) + goto nla_put_failure; + + if (addr->id != 0) + NLA_PUT_U8(nlmsg, MPTCP_PM_ADDR_ATTR_ID, addr->id); + if (addr->flags != 0) + NLA_PUT_U32(nlmsg, MPTCP_PM_ADDR_ATTR_FLAGS, addr->flags); + if (addr->ifindex != 0) + NLA_PUT_S32(nlmsg, MPTCP_PM_ADDR_ATTR_IF_IDX, addr->ifindex); + if (addr->port != 0) + NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_PORT, addr->port); + if (addr->addr_family != AF_UNSPEC) { + int addr_attr; + + NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_FAMILY, addr->addr_family); + if (addr->addr_family == AF_INET) + addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4; + else if (addr->addr_family == AF_INET6) + addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6; + else + g_return_val_if_reached(-NME_BUG); + NLA_PUT(nlmsg, addr_attr, nm_utils_addr_family_to_size(addr->addr_family), &addr->addr); + } + + NLA_NEST_END(nlmsg, nla_nest); + + nle = nl_send_auto(priv->sk_genl_sync, nlmsg); + if (nle < 0) { + _LOGT("mptcp: %s address %s: failed sending request: %s (%d)", + cmd_str, + nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)), + nm_strerror(nle), + nle); + return nle; + } + + do { + nle = nl_recvmsgs(priv->sk_genl_sync, &cb); + } while (nle == -EAGAIN); + + if (nle < 0) { + _LOGT("mptcp: %s address %s: failed: %s (%d)%s%s%s", + cmd_str, + nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)), + nm_strerror(nle), + nle, + NM_PRINT_FMT_QUOTED(extack_msg[0] != '\0', " \"", extack_msg, "\"", "")); + return nle; + } + + _LOGT("mptcp: %s address %s: success", + cmd_str, + nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf))); + + return 0; + +nla_put_failure: + g_return_val_if_reached(-NME_BUG); +} + +static GPtrArray * +mptcp_addrs_dump(NMPlatform *platform) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform); + gs_unref_ptrarray GPtrArray *addrs = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + FetchMptcpAddrParseData parse_data; + guint16 genl_family_id; + int r; + guint i; + + genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM); + if (genl_family_id == 0) { + _LOGT("mptcp: dump addresses fails because %s generic netlink family is unknown", + nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name); + return NULL; + } + + nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN)); + + if (!genlmsg_put(nlmsg, + NL_AUTO_PORT, + NL_AUTO_SEQ, + genl_family_id, + 0, + NLM_F_REQUEST | NLM_F_DUMP, + MPTCP_PM_CMD_GET_ADDR, + MPTCP_PM_VER)) + g_return_val_if_reached(NULL); + + r = nl_send_auto(priv->sk_genl_sync, nlmsg); + if (r < 0) { + _LOGT("mptcp: dump addresses failed to send dump request: %s", nm_strerror(r)); + return NULL; + } + + addrs = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + + parse_data = (FetchMptcpAddrParseData){ + .addrs = addrs, + }; + + nl_recvmsgs(priv->sk_genl_sync, + &((const struct nl_cb){ + .valid_cb = _mptcp_addrs_dump_parse_cb, + .valid_arg = (gpointer) &parse_data, + })); + + if (_LOGT_ENABLED()) { + _LOGT("mptcp: %u addresses dumped", addrs->len); + for (i = 0; i < addrs->len; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + _LOGT("mptcp: address[%04d]: %s", + i, + nmp_object_to_string(addrs->pdata[i], + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); + } + } + + return g_steal_pointer(&addrs); +} + +/*****************************************************************************/ + static void cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice) { @@ -9689,7 +10539,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)); } @@ -9721,43 +10573,53 @@ constructed(GObject *_object) nm_platform_get_use_udev(platform) ? "use" : "no", nm_platform_get_cache_tc(platform) ? "use" : "no"); - priv->genl = nl_socket_alloc(); - g_assert(priv->genl); + /*************************************************************************/ - nle = nl_connect(priv->genl, NETLINK_GENERIC); - if (nle) { - _LOGE("unable to connect the generic netlink socket \"%s\" (%d)", nm_strerror(nle), -nle); - nl_socket_free(priv->genl); - priv->genl = NULL; - } + nle = nl_socket_new(&priv->sk_genl_sync, NETLINK_GENERIC, NL_SOCKET_FLAGS_NONE, 0, 0); + g_assert(!nle); - priv->nlh = nl_socket_alloc(); - g_assert(priv->nlh); + _LOGD("genl: generic netlink socket for sync operations created: port=%u, fd=%d", + nl_socket_get_local_port(priv->sk_genl_sync), + nl_socket_get_fd(priv->sk_genl_sync)); - nle = nl_connect(priv->nlh, NETLINK_ROUTE); - g_assert(!nle); - nle = nl_socket_set_passcred(priv->nlh, 1); - g_assert(!nle); + /*************************************************************************/ - /* No blocking for event socket, so that we can drain it safely. */ - nle = nl_socket_set_nonblocking(priv->nlh); + /* 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); - /* use 8 MB for receive socket kernel queue. */ - nle = nl_socket_set_buffer_size(priv->nlh, 8 * 1024 * 1024, 0); + nle = nl_socket_add_memberships(priv->sk_genl, GENL_ID_CTRL, 0); g_assert(!nle); - nle = nl_socket_set_ext_ack(priv->nlh, TRUE); - if (nle) - _LOGD("could not enable extended acks on netlink socket"); + fd = nl_socket_get_fd(priv->sk_genl); - /* explicitly set the msg buffer size and disable MSG_PEEK. - * We use our own receive buffer priv->netlink_recv_buf. - * If we encounter NME_NL_MSG_TRUNC, we will increase the buffer - * and resync (as we would have lost the message without NL_MSG_PEEK). */ - nl_socket_disable_msg_peek(priv->nlh); + _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, + 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->nlh, + nle = nl_socket_add_memberships(priv->sk_rtnl, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV4_RULE, @@ -9769,37 +10631,32 @@ constructed(GObject *_object) g_assert(!nle); if (nm_platform_get_cache_tc(platform)) { - nle = nl_socket_add_memberships(priv->nlh, RTNLGRP_TC, 0); + nle = nl_socket_add_memberships(priv->sk_rtnl, RTNLGRP_TC, 0); nm_assert(!nle); } - fd = nl_socket_get_fd(priv->nlh); + fd = nl_socket_get_fd(priv->sk_rtnl); - _LOGD("Netlink socket for events established: port=%u, fd=%d", - nl_socket_get_local_port(priv->nlh), + _LOGD("rtnl: rtnetlink socket created: port=%u, fd=%d", + nl_socket_get_local_port(priv->sk_rtnl), fd); - priv->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, - event_handler, + _nl_event_handler_rtnl, platform); + /*************************************************************************/ + /* complete construction of the GObject instance before populating the cache. */ 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) { @@ -9881,6 +10738,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; @@ -9897,13 +10759,15 @@ 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); - nl_socket_free(priv->genl); + nm_clear_g_source_inst(&priv->event_source_genl); + nm_clear_g_source_inst(&priv->event_source_rtnl); - nm_clear_g_source_inst(&priv->event_source); - - nl_socket_free(priv->nlh); + nl_socket_free(priv->sk_genl_sync); + nl_socket_free(priv->sk_genl); + nl_socket_free(priv->sk_rtnl); { NM_G_MUTEX_LOCKED(&sysctl_clear_cache_lock); @@ -10021,4 +10885,8 @@ 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; + platform_class->mptcp_addr_update = mptcp_addr_update; + platform_class->mptcp_addrs_dump = mptcp_addrs_dump; } diff --git a/src/libnm-platform/nm-netlink.c b/src/libnm-platform/nm-netlink.c index 697ae591..36841d56 100644 --- a/src/libnm-platform/nm-netlink.c +++ b/src/libnm-platform/nm-netlink.c @@ -18,10 +18,15 @@ /*****************************************************************************/ -#define NL_SOCK_PASSCRED (1 << 1) -#define NL_MSG_PEEK (1 << 3) -#define NL_MSG_PEEK_EXPLICIT (1 << 4) -#define NL_NO_AUTO_ACK (1 << 5) +#define nm_assert_sk(sk) \ + G_STMT_START \ + { \ + const struct nl_sock *_sk = (sk); \ + \ + nm_assert(_sk); \ + nm_assert(_sk->s_fd >= 0); \ + } \ + G_STMT_END #ifndef NETLINK_EXT_ACK #define NETLINK_EXT_ACK 11 @@ -33,19 +38,20 @@ 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; }; struct nl_sock { struct sockaddr_nl s_local; struct sockaddr_nl s_peer; + size_t s_bufsize; int s_fd; int s_proto; unsigned int s_seq_next; unsigned int s_seq_expect; - int s_flags; - size_t s_bufsize; + bool s_msg_peek : 1; + bool s_auto_ack : 1; }; /*****************************************************************************/ @@ -71,13 +77,62 @@ NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str, NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"), NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), ); +static NM_UTILS_LOOKUP_STR_DEFINE(_rtnl_type_to_str, + guint16, + NM_UTILS_LOOKUP_DEFAULT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETLINK, "RTM_GETLINK"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWLINK, "RTM_NEWLINK"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELLINK, "RTM_DELLINK"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_SETLINK, "RTM_SETLINK"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETADDR, "RTM_GETADDR"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWADDR, "RTM_NEWADDR"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELADDR, "RTM_DELADDR"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETROUTE, "RTM_GETROUTE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWROUTE, "RTM_NEWROUTE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELROUTE, "RTM_DELROUTE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETRULE, "RTM_GETRULE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWRULE, "RTM_NEWRULE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELRULE, "RTM_DELRULE"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETQDISC, "RTM_GETQDISC"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWQDISC, "RTM_NEWQDISC"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELQDISC, "RTM_DELQDISC"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_GETTFILTER, "RTM_GETTFILTER"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_NEWTFILTER, "RTM_NEWTFILTER"), + NM_UTILS_LOOKUP_STR_ITEM(RTM_DELTFILTER, "RTM_DELTFILTER"), + NM_UTILS_LOOKUP_STR_ITEM(NLMSG_NOOP, "NLMSG_NOOP"), + NM_UTILS_LOOKUP_STR_ITEM(NLMSG_ERROR, "NLMSG_ERROR"), + NM_UTILS_LOOKUP_STR_ITEM(NLMSG_DONE, "NLMSG_DONE"), + NM_UTILS_LOOKUP_STR_ITEM(NLMSG_OVERRUN, "NLMSG_OVERRUN"), ); + +static NM_UTILS_LOOKUP_STR_DEFINE( + _genl_ctrl_cmd_to_str, + guint8, + NM_UTILS_LOOKUP_DEFAULT(NULL), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_UNSPEC, "CTRL_CMD_UNSPEC"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWFAMILY, "CTRL_CMD_NEWFAMILY"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELFAMILY, "CTRL_CMD_DELFAMILY"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETFAMILY, "CTRL_CMD_GETFAMILY"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWOPS, "CTRL_CMD_NEWOPS"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELOPS, "CTRL_CMD_DELOPS"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETOPS, "CTRL_CMD_GETOPS"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_NEWMCAST_GRP, "CTRL_CMD_NEWMCAST_GRP"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_DELMCAST_GRP, "CTRL_CMD_DELMCAST_GRP"), + NM_UTILS_LOOKUP_STR_ITEM(CTRL_CMD_GETMCAST_GRP, "CTRL_CMD_GETMCAST_GRP"), + /* CTRL_CMD_GETPOLICY was added in Linux 5.7 (released on 31 May, 2020), + * commit d07dcf9aadd6 ('netlink: add infrastructure to expose policies to userspace') */ + NM_UTILS_LOOKUP_STR_ITEM(10 /* CTRL_CMD_GETPOLICY */, "CTRL_CMD_GETPOLICY"), ); + /*****************************************************************************/ const char * -nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) +nl_nlmsghdr_to_str(int netlink_protocol, + guint32 pktinfo_group, + const struct nlmsghdr *hdr, + char *buf, + gsize len) { const char *b; - const char *s; + const char *s = NULL; guint flags, flags_before; const char *prefix; @@ -86,86 +141,46 @@ nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) b = buf; - switch (hdr->nlmsg_type) { - case RTM_GETLINK: - s = "RTM_GETLINK"; - break; - case RTM_NEWLINK: - s = "RTM_NEWLINK"; - break; - case RTM_DELLINK: - s = "RTM_DELLINK"; - break; - case RTM_SETLINK: - s = "RTM_SETLINK"; - break; - case RTM_GETADDR: - s = "RTM_GETADDR"; - break; - case RTM_NEWADDR: - s = "RTM_NEWADDR"; - break; - case RTM_DELADDR: - s = "RTM_DELADDR"; - break; - case RTM_GETROUTE: - s = "RTM_GETROUTE"; - break; - case RTM_NEWROUTE: - s = "RTM_NEWROUTE"; - break; - case RTM_DELROUTE: - s = "RTM_DELROUTE"; - break; - case RTM_GETRULE: - s = "RTM_GETRULE"; - break; - case RTM_NEWRULE: - s = "RTM_NEWRULE"; - break; - case RTM_DELRULE: - s = "RTM_DELRULE"; - break; - case RTM_GETQDISC: - s = "RTM_GETQDISC"; - break; - case RTM_NEWQDISC: - s = "RTM_NEWQDISC"; - break; - case RTM_DELQDISC: - s = "RTM_DELQDISC"; - break; - case RTM_GETTFILTER: - s = "RTM_GETTFILTER"; - break; - case RTM_NEWTFILTER: - s = "RTM_NEWTFILTER"; - break; - case RTM_DELTFILTER: - s = "RTM_DELTFILTER"; - break; - case NLMSG_NOOP: - s = "NLMSG_NOOP"; - break; - case NLMSG_ERROR: - s = "NLMSG_ERROR"; - break; - case NLMSG_DONE: - s = "NLMSG_DONE"; - break; - case NLMSG_OVERRUN: - s = "NLMSG_OVERRUN"; + switch (netlink_protocol) { + case NETLINK_ROUTE: + s = _rtnl_type_to_str(hdr->nlmsg_type); + if (s) + nm_strbuf_append_str(&buf, &len, s); + else + nm_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); break; default: + nm_assert_not_reached(); + /* fall-through */ + case NETLINK_GENERIC: + if (pktinfo_group == 0) + nm_strbuf_append(&buf, &len, "group:unicast"); + else + nm_strbuf_append(&buf, &len, "group:multicast(%u)", (unsigned) pktinfo_group); + s = NULL; + if (hdr->nlmsg_type == GENL_ID_CTRL) + s = "GENL_ID_CTRL"; + if (s) + nm_strbuf_append(&buf, &len, ", msg-type:%s", s); + else + nm_strbuf_append(&buf, &len, ", msg-type:(%u)", (unsigned) hdr->nlmsg_type); + + if (genlmsg_valid_hdr(hdr, 0)) { + const struct genlmsghdr *ghdr; + + ghdr = nlmsg_data(hdr); + s = NULL; + if (hdr->nlmsg_type == GENL_ID_CTRL) + s = _genl_ctrl_cmd_to_str(ghdr->cmd); + if (s) + nm_strbuf_append(&buf, &len, ", cmd:%s", s); + else + nm_strbuf_append(&buf, &len, ", cmd:(%u)", (unsigned) ghdr->cmd); + } break; } - if (s) - nm_strbuf_append_str(&buf, &len, s); - else - nm_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - flags = hdr->nlmsg_flags; if (!flags) { @@ -198,27 +213,30 @@ nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) if (flags_before != flags) prefix = ";"; - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F(NLM_F_REPLACE, "replace"); - _F(NLM_F_EXCL, "excl"); - _F(NLM_F_CREATE, "create"); - _F(NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F(NLM_F_DUMP, "dump"); - _F(NLM_F_ROOT, "root"); - _F(NLM_F_MATCH, "match"); - _F(NLM_F_ATOMIC, "atomic"); - break; + switch (netlink_protocol) { + case NETLINK_ROUTE: + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + case RTM_NEWQDISC: + case RTM_NEWTFILTER: + _F(NLM_F_REPLACE, "replace"); + _F(NLM_F_EXCL, "excl"); + _F(NLM_F_CREATE, "create"); + _F(NLM_F_APPEND, "append"); + break; + case RTM_GETLINK: + case RTM_GETADDR: + case RTM_GETROUTE: + case RTM_DELQDISC: + case RTM_DELTFILTER: + _F(NLM_F_DUMP, "dump"); + _F(NLM_F_ROOT, "root"); + _F(NLM_F_MATCH, "match"); + _F(NLM_F_ATOMIC, "atomic"); + break; + } } #undef _F @@ -237,29 +255,32 @@ flags_done: /*****************************************************************************/ struct nlmsghdr * -nlmsg_hdr(struct nl_msg *n) +nlmsg_hdr(const struct nl_msg *n) { return n->nm_nlh; } 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; - - nm_assert(pad >= 0); + char *buf = (char *) n->nm_nlh; + uint32_t tlen; - if (len > n->nm_size) - return NULL; + nm_assert(n); + nm_assert(pad == 0 || nm_utils_is_power_of_two(pad)); + nm_assert(n->nm_nlh->nlmsg_len <= n->nm_size); - tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + if (pad != 0) { + tlen = (len + (pad - 1u)) & ~(pad - 1u); + if (tlen < len) + return NULL; + } else + tlen = len; - if ((tlen + nlmsg_len) > n->nm_size) + if (tlen > n->nm_size - n->nm_nlh->nlmsg_len) return NULL; - buf += nlmsg_len; + buf += n->nm_nlh->nlmsg_len; n->nm_nlh->nlmsg_len += tlen; if (tlen > len) @@ -270,6 +291,52 @@ nlmsg_reserve(struct nl_msg *n, size_t len, int pad) /*****************************************************************************/ +int +nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg) +{ + const struct nlmsgerr *e; + + nm_assert(nlh); + + NM_SET_OUT(out_extack_msg, NULL); + + if (nlh->nlmsg_type != NLMSG_ERROR) + return -NME_NL_MSG_INVAL; + + if (nlh->nlmsg_len < nlmsg_size(sizeof(struct nlmsgerr))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + return -NME_NL_MSG_TRUNC; + } + + e = nlmsg_data(nlh); + + if (!e->error) + return 0; + + if (NM_FLAGS_HAS(nlh->nlmsg_flags, NLM_F_ACK_TLVS) && out_extack_msg + && nlh->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) { + static const struct nla_policy policy[] = { + [NLMSGERR_ATTR_MSG] = {.type = NLA_STRING}, + [NLMSGERR_ATTR_OFFS] = {.type = NLA_U32}, + }; + struct nlattr *tb[G_N_ELEMENTS(policy)]; + struct nlattr *tlvs; + + tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN); + if (nla_parse_arr(tb, tlvs, nlh->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, policy) >= 0) { + if (tb[NLMSGERR_ATTR_MSG]) + *out_extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]); + } + } + + return -nm_errno_from_native(e->error); +} + +/*****************************************************************************/ + struct nlattr * nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) { @@ -304,6 +371,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){ @@ -341,7 +410,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; @@ -366,27 +435,27 @@ 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; } /*****************************************************************************/ int -nlmsg_parse(struct nlmsghdr *nlh, +nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, @@ -399,14 +468,17 @@ nlmsg_parse(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; @@ -547,7 +619,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; @@ -566,14 +639,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; @@ -588,6 +665,7 @@ nla_nest_end(struct nl_msg *msg, struct nlattr *start) static const uint8_t nla_attr_minlen[NLA_TYPE_MAX + 1] = { [NLA_U8] = sizeof(uint8_t), [NLA_U16] = sizeof(uint16_t), + [NLA_S32] = sizeof(int32_t), [NLA_U32] = sizeof(uint32_t), [NLA_U64] = sizeof(uint64_t), [NLA_STRING] = 1, @@ -712,9 +790,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) { @@ -745,8 +823,8 @@ genlmsg_user_hdr(const struct genlmsghdr *gnlh) return genlmsg_data(gnlh); } -struct genlmsghdr * -genlmsg_hdr(struct nlmsghdr *nlh) +const struct genlmsghdr * +genlmsg_hdr(const struct nlmsghdr *nlh) { return nlmsg_data(nlh); } @@ -779,7 +857,7 @@ genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) } int -genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) +genlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) { struct genlmsghdr *ghdr; @@ -794,13 +872,13 @@ genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) } int -genlmsg_parse(struct nlmsghdr *nlh, +genlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { - struct genlmsghdr *ghdr; + const struct genlmsghdr *ghdr; if (!genlmsg_valid_hdr(nlh, hdrlen)) return -NME_NL_MSG_TOOSHORT; @@ -813,23 +891,24 @@ genlmsg_parse(struct nlmsghdr *nlh, policy); } +const struct nla_policy genl_ctrl_policy[CTRL_ATTR_MCAST_GROUPS + 1] = { + [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, + [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, + [CTRL_ATTR_VERSION] = {.type = NLA_U32}, + [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, + [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, + [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, + [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, +}; + static int -_genl_parse_getfamily(struct nl_msg *msg, void *arg) +_genl_parse_getfamily(const struct nl_msg *msg, void *arg) { - static const struct nla_policy ctrl_policy[] = { - [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, - [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, - [CTRL_ATTR_VERSION] = {.type = NLA_U32}, - [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, - [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, - [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, - [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, - }; - struct nlattr *tb[G_N_ELEMENTS(ctrl_policy)]; + struct nlattr *tb[G_N_ELEMENTS(genl_ctrl_policy)]; struct nlmsghdr *nlh = nlmsg_hdr(msg); gint32 *response_data = arg; - if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0) + if (genlmsg_parse_arr(nlh, 0, tb, genl_ctrl_policy) < 0) return NL_SKIP; if (tb[CTRL_ATTR_FAMILY_ID]) @@ -879,30 +958,14 @@ genl_ctrl_resolve(struct nl_sock *sk, const char *name) /*****************************************************************************/ -struct nl_sock * -nl_socket_alloc(void) -{ - struct nl_sock *sk; - - sk = g_slice_new0(struct nl_sock); - - sk->s_fd = -1; - sk->s_local.nl_family = AF_NETLINK; - sk->s_peer.nl_family = AF_NETLINK; - sk->s_seq_expect = sk->s_seq_next = time(NULL); - - return sk; -} - void nl_socket_free(struct nl_sock *sk) { if (!sk) return; - if (sk->s_fd >= 0) - nm_close(sk->s_fd); - g_slice_free(struct nl_sock, sk); + nm_close(sk->s_fd); + nm_g_slice_free(sk); } int @@ -928,18 +991,24 @@ nl_socket_set_passcred(struct nl_sock *sk, int state) { int err; - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; + nm_assert_sk(sk); err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); if (err < 0) return -nm_errno_from_native(errno); + return 0; +} - if (state) - sk->s_flags |= NL_SOCK_PASSCRED; - else - sk->s_flags &= ~NL_SOCK_PASSCRED; +int +nl_socket_set_pktinfo(struct nl_sock *sk, int state) +{ + int err; + nm_assert_sk(sk); + + err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO, &state, sizeof(state)); + if (err < 0) + return -nm_errno_from_native(errno); return 0; } @@ -960,8 +1029,7 @@ nlmsg_get_dst(struct nl_msg *msg) int nl_socket_set_nonblocking(const struct nl_sock *sk) { - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; + nm_assert_sk(sk); if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) return -nm_errno_from_native(errno); @@ -974,15 +1042,14 @@ nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) { int err; + nm_assert_sk(sk); + if (rxbuf <= 0) rxbuf = 32768; if (txbuf <= 0) txbuf = 32768; - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); if (err < 0) { return -nm_errno_from_native(errno); @@ -1002,8 +1069,7 @@ nl_socket_add_memberships(struct nl_sock *sk, int group, ...) int err; va_list ap; - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; + nm_assert_sk(sk); va_start(ap, group); @@ -1029,85 +1095,96 @@ nl_socket_add_memberships(struct nl_sock *sk, int group, ...) return 0; } +/*****************************************************************************/ + int -nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable) +nl_socket_new(struct nl_sock **out_sk, + int protocol, + NLSocketFlags flags, + int bufsize_rx, + int bufsize_tx) { - int err, val; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - val = !!enable; - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); - if (err < 0) + nm_auto_nlsock struct nl_sock *sk = NULL; + nm_auto_close int fd = -1; + time_t t; + int err; + int nmerr; + socklen_t addrlen; + struct sockaddr_nl local = {0}; + int i_val; + + nm_assert(out_sk && !*out_sk); + + fd = socket(AF_NETLINK, + SOCK_RAW | SOCK_CLOEXEC + | (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_NONBLOCK) ? SOCK_NONBLOCK : 0), + protocol); + if (fd < 0) return -nm_errno_from_native(errno); - return 0; -} - -void -nl_socket_disable_msg_peek(struct nl_sock *sk) -{ - sk->s_flags |= NL_MSG_PEEK_EXPLICIT; - sk->s_flags &= ~NL_MSG_PEEK; -} + t = time(NULL); + + sk = g_slice_new(struct nl_sock); + *sk = (struct nl_sock){ + .s_fd = nm_steal_fd(&fd), + .s_local = + { + .nl_pid = 0, + .nl_family = AF_NETLINK, + .nl_groups = 0, + }, + .s_peer = + { + .nl_pid = 0, + .nl_family = AF_NETLINK, + .nl_groups = 0, + }, + .s_seq_expect = t, + .s_seq_next = t, + .s_bufsize = 0, + .s_msg_peek = !NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_DISABLE_MSG_PEEK), + .s_auto_ack = TRUE, + }; -int -nl_connect(struct nl_sock *sk, int protocol) -{ - int err, nmerr; - socklen_t addrlen; - struct sockaddr_nl local = {0}; + nmerr = nl_socket_set_buffer_size(sk, bufsize_rx, bufsize_tx); + if (nmerr < 0) + return nmerr; - if (sk->s_fd != -1) - return -NME_NL_BAD_SOCK; + i_val = 1; + (void) setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &i_val, sizeof(i_val)); - sk->s_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); - if (sk->s_fd < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; + if (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_PASSCRED)) { + err = nl_socket_set_passcred(sk, 1); + if (err < 0) + return err; } - nmerr = nl_socket_set_buffer_size(sk, 0, 0); - if (nmerr < 0) - goto errout; - - nm_assert(sk->s_local.nl_pid == 0); + if (NM_FLAGS_HAS(flags, NL_SOCKET_FLAGS_PKTINFO)) { + err = nl_socket_set_pktinfo(sk, 1); + if (err < 0) + return err; + } err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local)); - if (err != 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } + if (err != 0) + return -nm_errno_from_native(errno); addrlen = sizeof(local); err = getsockname(sk->s_fd, (struct sockaddr *) &local, &addrlen); - if (err < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } + if (err < 0) + return -nm_errno_from_native(errno); - if (addrlen != sizeof(local)) { - nmerr = -NME_UNSPEC; - goto errout; - } + if (addrlen != sizeof(local)) + return -NME_UNSPEC; - if (local.nl_family != AF_NETLINK) { - nmerr = -NME_UNSPEC; - goto errout; - } + if (local.nl_family != AF_NETLINK) + return -NME_UNSPEC; sk->s_local = local; sk->s_proto = protocol; + *out_sk = g_steal_pointer(&sk); return 0; - -errout: - if (sk->s_fd != -1) { - close(sk->s_fd); - sk->s_fd = -1; - } - return nmerr; } /*****************************************************************************/ @@ -1124,7 +1201,7 @@ _cb_init(struct nl_cb *dst, const struct nl_cb *src) } static int -ack_wait_handler(struct nl_msg *msg, void *arg) +ack_wait_handler(const struct nl_msg *msg, void *arg) { return NL_STOP; } @@ -1176,7 +1253,7 @@ nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb) gboolean creds_has; continue_reading: - n = nl_recv(sk, NULL, 0, &nla, &buf, &creds, &creds_has); + n = nl_recv(sk, NULL, 0, &nla, &buf, &creds, &creds_has, NULL, NULL); if (n <= 0) return n; @@ -1193,7 +1270,7 @@ continue_reading: nrecv++; /* Only do sequence checking if auto-ack mode is enabled */ - if (!(sk->s_flags & NL_NO_AUTO_ACK)) { + if (sk->s_auto_ack) { if (hdr->nlmsg_seq != sk->s_seq_expect) { nmerr = -NME_NL_SEQ_MISMATCH; goto out; @@ -1378,7 +1455,7 @@ nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) nlh->nlmsg_flags |= NLM_F_REQUEST; - if (!(sk->s_flags & NL_NO_AUTO_ACK)) + if (sk->s_auto_ack) nlh->nlmsg_flags |= NLM_F_ACK; } @@ -1413,6 +1490,10 @@ nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) * on success. * @out_creds_has: (out) (allow-none): result indicating whether * @out_creds was filled. +* @out_pktinfo_group: (out) (allow-none): optional out buffer for NETLINK_PKTINFO +* group on success. + * @out_pktinfo_has: (out) (allow-none): result indicating whether + * @out_pktinfo_group was filled. * * If @buf0_len is zero, the function will g_malloc() a new receive buffer of size * nl_socket_get_msg_buf_size(). If @buf0_len is larger than zero, then @buf0 @@ -1435,18 +1516,20 @@ nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred *out_creds, - gboolean *out_creds_has) + gboolean *out_creds_has, + uint32_t *out_pktinfo_group, + gboolean *out_pktinfo_has) { - /* We really expect msg_contol_buf to be large enough and MSG_CTRUNC not - * happening. We nm_assert() against that. However, in release builds - * we don't assert, so add some extra safety space for the unexpected - * case where we might need more than CMSG_SPACE(sizeof(struct ucred)). - * It should not hurt and should not be necessary. It's just some - * extra defensive space. */ -#define _MSG_CONTROL_BUF_EXTRA_SPACE (NM_MORE_ASSERTS ? 512u : 0u) union { - struct cmsghdr cmsghdr; - char buf[CMSG_SPACE(sizeof(struct ucred)) + _MSG_CONTROL_BUF_EXTRA_SPACE]; + struct cmsghdr _dummy_for_alignment; + struct { + char buf[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(struct nl_pktinfo))]; + + /* We really expect that "buf" is large enough end even assert against + * that. We don't expect and don't want to handle MSG_CTRUNC error. + * Still, add some extra safety. This is on the stack and essentially for free. */ + char _extra[512]; + }; } msg_contol_buf; ssize_t n; int flags = 0; @@ -1459,17 +1542,16 @@ nl_recv(struct nl_sock *sk, .msg_controllen = 0, .msg_control = NULL, }; - struct ucred tmpcreds; - gboolean tmpcreds_has = FALSE; - int retval; - int errsv; + struct cmsghdr *cmsg; + int retval; + int errsv; nm_assert(nla); nm_assert(buf && !*buf); - nm_assert(!out_creds_has == !out_creds); + nm_assert(!out_creds_has || out_creds); + nm_assert(!out_pktinfo_has || out_pktinfo_group); - if ((sk->s_flags & NL_MSG_PEEK) - || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) + if (sk->s_msg_peek) flags |= MSG_PEEK | MSG_TRUNC; if (buf0_len > 0) { @@ -1480,7 +1562,7 @@ nl_recv(struct nl_sock *sk, iov.iov_base = g_malloc(iov.iov_len); } - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { + if (out_creds_has || out_pktinfo_has) { msg.msg_controllen = sizeof(msg_contol_buf); msg.msg_control = msg_contol_buf.buf; } @@ -1500,11 +1582,14 @@ retry: goto abort; } + nm_assert((gsize) n <= G_MAXINT); + /* We really don't expect truncation of ancillary data. We provided a large * enough buffer, so this is likely a bug. In the worst case, we might lack * the requested credentials and the caller likely will reject the message * later. */ nm_assert(!(msg.msg_flags & MSG_CTRUNC)); + nm_assert(msg.msg_controllen <= G_STRUCT_OFFSET(typeof(msg_contol_buf), _extra)); if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { /* respond with error to an incomplete message */ @@ -1533,32 +1618,35 @@ retry: goto abort; } - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - struct cmsghdr *cmsg; - + if (out_creds_has || out_pktinfo_has) { + NM_SET_OUT(out_creds_has, FALSE); + NM_SET_OUT(out_pktinfo_has, FALSE); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - if (cmsg->cmsg_type != SCM_CREDENTIALS) - continue; - memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds)); - tmpcreds_has = TRUE; - break; + switch (cmsg->cmsg_level) { + case SOL_SOCKET: + if (cmsg->cmsg_type == SCM_CREDENTIALS && out_creds_has) { + memcpy(out_creds, CMSG_DATA(cmsg), sizeof(*out_creds)); + *out_creds_has = TRUE; + } + break; + case SOL_NETLINK: + if (cmsg->cmsg_type == NETLINK_PKTINFO && out_pktinfo_has) { + struct nl_pktinfo p; + + memcpy(&p, CMSG_DATA(cmsg), sizeof(p)); + *out_pktinfo_group = p.group; + *out_pktinfo_has = TRUE; + } + break; + } } } - retval = n; + *buf = iov.iov_base; + return (int) n; abort: - if (retval <= 0) { - if (iov.iov_base != buf0) - g_free(iov.iov_base); - return retval; - } - - *buf = iov.iov_base; - if (out_creds && tmpcreds_has) - *out_creds = tmpcreds; - NM_SET_OUT(out_creds_has, tmpcreds_has); + if (iov.iov_base != buf0) + g_free(iov.iov_base); return retval; } diff --git a/src/libnm-platform/nm-netlink.h b/src/libnm-platform/nm-netlink.h index bb1e41ee..44ed70af 100644 --- a/src/libnm-platform/nm-netlink.h +++ b/src/libnm-platform/nm-netlink.h @@ -31,6 +31,7 @@ enum { NLA_UNSPEC, /* Unspecified type, binary data chunk */ NLA_U8, /* 8 bit integer */ NLA_U16, /* 16 bit integer */ + NLA_S32, /* 32 bit integer */ NLA_U32, /* 32 bit integer */ NLA_U64, /* 64 bit integer */ NLA_STRING, /* NUL terminated character string */ @@ -43,13 +44,37 @@ enum { struct nl_msg; +/* This is similar to "struct nl_msg", in that it contains a + * netlink message including additional information like the + * src, creds, protocol. + * + * The difference is that "struct nl_msg" is an opaque type and + * contains a copy of the message (requiring two heap allocations). + * "struct nl_msg_lite" can be on the stack and it can directly + * point to the receive buffer, without need to copy the message. + * That can be useful, if you don't need to clone the message and + * just need to pass it "down the stack" for somebody to parse + * the message. */ +struct nl_msg_lite { + int nm_protocol; + const struct sockaddr_nl *nm_src; + const struct sockaddr_nl *nm_dst; + const struct ucred *nm_creds; + const struct nlmsghdr *nm_nlh; + uint32_t nm_size; +}; + /*****************************************************************************/ const char *nl_nlmsgtype2str(int type, char *buf, size_t size); const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len); -const char *nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len); +const char *nl_nlmsghdr_to_str(int netlink_protocol, + guint32 pktinfo_group, + const struct nlmsghdr *hdr, + char *buf, + gsize len); /*****************************************************************************/ @@ -267,20 +292,21 @@ nla_put_uint32(struct nl_msg *msg, int attrtype, uint32_t val) return nla_put(msg, attrtype, sizeof(val), &val); } -#define NLA_PUT(msg, attrtype, attrlen, data) \ - G_STMT_START \ - { \ - if (nla_put(msg, attrtype, attrlen, data) < 0) \ - goto nla_put_failure; \ - } \ +#define NLA_PUT(msg, attrtype, attrlen, data) \ + G_STMT_START \ + { \ + if (nla_put((msg), (attrtype), (attrlen), (data)) < 0) \ + goto nla_put_failure; \ + } \ G_STMT_END -#define NLA_PUT_TYPE(msg, type, attrtype, value) \ - G_STMT_START \ - { \ - type __nla_tmp = value; \ - NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \ - } \ +#define NLA_PUT_TYPE(msg, type, attrtype, value) \ + G_STMT_START \ + { \ + type const _nla_tmp = value; \ + \ + NLA_PUT((msg), (attrtype), sizeof(_nla_tmp), &_nla_tmp); \ + } \ G_STMT_END #define NLA_PUT_U8(msg, attrtype, value) NLA_PUT_TYPE(msg, uint8_t, attrtype, value) @@ -328,6 +354,14 @@ void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr); struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype); int nla_nest_end(struct nl_msg *msg, struct nlattr *start); +#define NLA_NEST_END(msg, nest_start) \ + G_STMT_START \ + { \ + if (nla_nest_end((msg), (nest_start)) < 0) \ + goto nla_put_failure; \ + } \ + G_STMT_END + int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, @@ -365,13 +399,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); @@ -413,26 +447,29 @@ void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr); struct ucred *nlmsg_get_creds(struct nl_msg *msg); void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds); -static inline void -_nm_auto_nl_msg_cleanup(struct nl_msg **ptr) +NM_AUTO_DEFINE_FCN0(struct nl_msg *, _nm_auto_nl_msg_cleanup, nlmsg_free); +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) + +static inline const struct nlmsghdr * +nlmsg_undata(const void *data) { - nlmsg_free(*ptr); + /* from the data, get back the header. It's the inverse of nlmsg_data(). */ + return (void *) (((unsigned char *) data) - NLMSG_HDRLEN); } -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) static inline void * nlmsg_data(const struct nlmsghdr *nlh) { - return (unsigned char *) nlh + NLMSG_HDRLEN; + return ((unsigned char *) nlh) + NLMSG_HDRLEN; } static inline void * nlmsg_tail(const struct nlmsghdr *nlh) { - return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); + return ((unsigned char *) nlh) + NLMSG_ALIGN(nlh->nlmsg_len); } -struct nlmsghdr *nlmsg_hdr(struct nl_msg *n); +struct nlmsghdr *nlmsg_hdr(const struct nl_msg *n); static inline int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) @@ -468,7 +505,9 @@ nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); } -int nlmsg_parse(struct nlmsghdr *nlh, +int nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg); + +int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, @@ -482,20 +521,41 @@ int nlmsg_parse(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); /*****************************************************************************/ +typedef enum { + NL_SOCKET_FLAGS_NONE = 0, + NL_SOCKET_FLAGS_NONBLOCK = 0x1, + NL_SOCKET_FLAGS_PASSCRED = 0x2, + NL_SOCKET_FLAGS_PKTINFO = 0x4, + NL_SOCKET_FLAGS_DISABLE_MSG_PEEK = 0x8, + + _NL_SOCKET_FLAGS_ALL = (NL_SOCKET_FLAGS_DISABLE_MSG_PEEK << 1) - 1, +} NLSocketFlags; + #define NL_AUTO_PORT 0 #define NL_AUTO_SEQ 0 struct nl_sock; -struct nl_sock *nl_socket_alloc(void); +int nl_socket_new(struct nl_sock **out_sk, + int protocol, + NLSocketFlags flags, + int bufsize_rx, + int bufsize_tx); void nl_socket_free(struct nl_sock *sk); +NM_AUTO_DEFINE_FCN0(struct nl_sock *, _nm_auto_nlsock, nl_socket_free); +#define nm_auto_nlsock nm_auto(_nm_auto_nlsock) + int nl_socket_get_fd(const struct nl_sock *sk); struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg); @@ -507,9 +567,9 @@ int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf); int nl_socket_set_passcred(struct nl_sock *sk, int state); -int nl_socket_set_nonblocking(const struct nl_sock *sk); +int nl_socket_set_pktinfo(struct nl_sock *sk, int state); -void nl_socket_disable_msg_peek(struct nl_sock *sk); +int nl_socket_set_nonblocking(const struct nl_sock *sk); uint32_t nl_socket_get_local_port(const struct nl_sock *sk); @@ -523,7 +583,9 @@ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred *out_creds, - gboolean *out_creds_has); + gboolean *out_creds_has, + uint32_t *out_pktinfo_group, + gboolean *out_pktinfo_has); int nl_send(struct nl_sock *sk, struct nl_msg *msg); @@ -540,9 +602,11 @@ enum nl_cb_action { NL_STOP, }; -typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); +typedef int (*nl_recvmsg_msg_cb_t)(const struct nl_msg *msg, void *arg); -typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); +typedef int (*nl_recvmsg_err_cb_t)(const struct sockaddr_nl *nla, + const struct nlmsgerr *nlerr, + void *arg); struct nl_cb { nl_recvmsg_msg_cb_t valid_cb; @@ -568,28 +632,28 @@ int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb); int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb); -int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable); - /*****************************************************************************/ -void *genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version); -void *genlmsg_data(const struct genlmsghdr *gnlh); -void *genlmsg_user_hdr(const struct genlmsghdr *gnlh); -struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh); -void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); -struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_len(const struct genlmsghdr *gnlh); -int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen); - -int genlmsg_parse(struct nlmsghdr *nlh, +extern const struct nla_policy genl_ctrl_policy[8]; + +void *genlmsg_put(struct nl_msg *msg, + uint32_t port, + uint32_t seq, + uint16_t family, + uint32_t hdrlen, + uint16_t flags, + uint8_t cmd, + uint8_t version); +void *genlmsg_data(const struct genlmsghdr *gnlh); +void *genlmsg_user_hdr(const struct genlmsghdr *gnlh); +const struct genlmsghdr *genlmsg_hdr(const struct nlmsghdr *nlh); +void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_len(const struct genlmsghdr *gnlh); +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); +int genlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen); + +int genlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 090af26d..df177485 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -7,6 +7,8 @@ #include "nm-platform.h" +#include "libnm-std-aux/nm-linux-compat.h" + #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> @@ -113,24 +115,29 @@ nmp_link_address_get_as_bytes(const NMPLinkAddress *addr) #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" -#define NMLOG_COMMON(level, name, ...) \ - char __prefix[32]; \ - const char *__p_prefix = _NMLOG_PREFIX_NAME; \ - const NMPlatform *const __self = (self); \ - const char *__name = name; \ - \ - if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ - g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ - __p_prefix = __prefix; \ - } \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - __name, \ - NULL, \ - "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __p_prefix, \ - NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); +#define NMLOG_COMMON(level, name, ...) \ + G_STMT_START \ + { \ + char __prefix[32]; \ + const char *__p_prefix = _NMLOG_PREFIX_NAME; \ + const NMPlatform *const __self = (self); \ + const char *__name = name; \ + \ + if (__self && NM_PLATFORM_GET_PRIVATE(__self)->log_with_ptr) { \ + g_snprintf(__prefix, sizeof(__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \ + __p_prefix = __prefix; \ + } \ + _nm_log((level), \ + _NMLOG_DOMAIN, \ + 0, \ + __name, \ + NULL, \ + "%s: %s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + __p_prefix, \ + NM_PRINT_FMT_QUOTED(__name, "(", __name, ") ", "") \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END #define _NMLOG(level, ...) \ G_STMT_START \ @@ -395,6 +402,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_NAME, + }, + [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 @@ -1292,6 +1369,12 @@ nm_platform_link_add(NMPlatform *self, buf_p, buf_len); break; + case NM_LINK_TYPE_BOND: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data, + buf_p, + buf_len); + break; default: nm_assert(!extra_data); break; @@ -1332,6 +1415,12 @@ nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpo buf_p, buf_len); break; + case NM_LINK_TYPE_BOND: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_bond_to_string((const NMPlatformLnkBond *) extra_data, + buf_p, + buf_len); + break; default: nm_assert(!extra_data); break; @@ -2232,6 +2321,12 @@ _link_get_lnk(NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatf return lnk ? &lnk->object : NULL; } +const NMPlatformLnkBond * +nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) +{ + return _link_get_lnk(self, ifindex, NM_LINK_TYPE_BOND, out_link); +} + const NMPlatformLnkBridge * nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link) { @@ -4133,7 +4228,7 @@ nm_platform_ip_address_sync(NMPlatform *self, * we check the "secondary" flag. */ plat_addresses = nm_platform_lookup_clone( self, - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), NULL, NULL); @@ -4436,18 +4531,20 @@ next_plat:; nm_platform_ip4_broadcast_address_from_addr(&known_address->a4), lifetime, preferred, - IFA_F_NOPREFIXROUTE, + known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE, known_address->a4.label)) success = FALSE; } else { - if (!nm_platform_ip6_address_add(self, - ifindex, - known_address->a6.address, - known_address->a6.plen, - known_address->a6.peer_address, - lifetime, - preferred, - IFA_F_NOPREFIXROUTE | known_address->a6.n_ifa_flags)) + if (!nm_platform_ip6_address_add( + self, + ifindex, + known_address->a6.address, + known_address->a6.plen, + known_address->a6.peer_address, + lifetime, + preferred, + (known_address->ax.a_no_auto_noprefixroute ? 0 : IFA_F_NOPREFIXROUTE) + | known_address->a6.n_ifa_flags)) success = FALSE; } } @@ -4545,7 +4642,9 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, GPtrArray *result = NULL; CList *iter; - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), + ifindex); head_entry = nm_platform_lookup(self, &lookup); @@ -4626,7 +4725,9 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL, NM_IP_ROUTE_TABLE_SYNC_MODE_ALL_PRUNE)); - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, + NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), + ifindex); head_entry = nm_platform_lookup(self, &lookup); if (!head_entry) return NULL; @@ -5226,23 +5327,26 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) _CHECK_SELF(self, klass, FALSE); - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_ROUTING_RULE: - _LOGD("%s: delete %s", - NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - case NMP_OBJECT_TYPE_QDISC: - case NMP_OBJECT_TYPE_TFILTER: - ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex; - _LOG3D("%s: delete %s", - NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - break; - default: - g_return_val_if_reached(FALSE); + if (_LOGD_ENABLED()) { + switch (NMP_OBJECT_GET_TYPE(obj)) { + case NMP_OBJECT_TYPE_ROUTING_RULE: + case NMP_OBJECT_TYPE_MPTCP_ADDR: + _LOGD("%s: delete %s", + NMP_OBJECT_GET_CLASS(obj)->obj_type_name, + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + case NMP_OBJECT_TYPE_QDISC: + case NMP_OBJECT_TYPE_TFILTER: + ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex; + _LOG3D("%s: delete %s", + NMP_OBJECT_GET_CLASS(obj)->obj_type_name, + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + break; + default: + g_return_val_if_reached(FALSE); + } } return klass->object_delete(self, obj); @@ -5972,6 +6076,123 @@ nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsiz } const char * +nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len) +{ + char sbuf_miimon[30]; + char sbuf_updelay[30]; + char sbuf_downdelay[30]; + char sbuf_peer_notif_delay[60]; + char sbuf_arp_all_targets[30]; + char sbuf_resend_igmp[30]; + char sbuf_lp_interval[30]; + char sbuf_tlb_dynamic_lb[30]; + int i; + + if (!nm_utils_to_string_buffer_init_null(lnk, &buf, &len)) + return buf; + + nm_strbuf_append( + &buf, + &len, + "bond" + " mode %u" + " primary %u" + "%s" /* miimon */ + "%s" /* updelay */ + "%s" /* downdelay */ + " arp_interval %u" + "%s" /* resend_igmp */ + " min_links %u" + "%s" /* lp_interval */ + " packets_per_port %u" + "%s" /* peer_notif_delay */ + "%s" /* arp_all_targets */ + " arp_validate %u" + " ad_actor_sys_prio %u" + " ad_user_port_key %u" + " ad_actor_system " NM_ETHER_ADDR_FORMAT_STR "" + " primary_reselect %u" + " fail_over_mac %u" + " xmit_hash_policy %u" + " num_gray_arp %u" + " all_ports_active %u" + " lacp_rate %u" + " ad_select %u" + " use_carrier %d" + "%s" /* tlb_dynamic_lb */, + lnk->mode, + lnk->primary, + lnk->miimon_has || lnk->miimon != 0 + ? nm_sprintf_buf(sbuf_miimon, " miimon%s %u", !lnk->miimon_has ? "?" : "", lnk->miimon) + : "", + lnk->updelay_has || lnk->updelay != 0 ? nm_sprintf_buf(sbuf_updelay, + " updelay%s %u", + !lnk->updelay_has ? "?" : "", + lnk->updelay) + : "", + lnk->downdelay_has || lnk->downdelay != 0 ? nm_sprintf_buf(sbuf_downdelay, + " downdelay%s %u", + !lnk->downdelay_has ? "?" : "", + lnk->downdelay) + : "", + lnk->arp_interval, + lnk->resend_igmp_has || lnk->resend_igmp != 0 + ? nm_sprintf_buf(sbuf_resend_igmp, + " resend_igmp%s %u", + !lnk->resend_igmp_has ? "?" : "", + lnk->resend_igmp) + : "", + lnk->min_links, + lnk->lp_interval_has || lnk->lp_interval != 1 + ? nm_sprintf_buf(sbuf_lp_interval, + " lp_interval%s %u", + !lnk->lp_interval_has ? "?" : "", + lnk->lp_interval) + : "", + lnk->packets_per_port, + lnk->peer_notif_delay_has || lnk->peer_notif_delay != 0 + ? nm_sprintf_buf(sbuf_peer_notif_delay, + " peer_notif_delay%s %u", + !lnk->peer_notif_delay_has ? "?" : "", + lnk->peer_notif_delay) + : "", + lnk->arp_all_targets_has || lnk->arp_all_targets != 0 + ? nm_sprintf_buf(sbuf_arp_all_targets, + " arp_all_targets%s %u", + !lnk->arp_all_targets_has ? "?" : "", + lnk->arp_all_targets) + : "", + lnk->arp_validate, + lnk->ad_actor_sys_prio, + lnk->ad_user_port_key, + NM_ETHER_ADDR_FORMAT_VAL(&lnk->ad_actor_system), + lnk->primary_reselect, + lnk->fail_over_mac, + lnk->xmit_hash_policy, + lnk->num_grat_arp, + lnk->all_ports_active, + lnk->lacp_rate, + lnk->ad_select, + (int) lnk->use_carrier, + lnk->tlb_dynamic_lb_has ? nm_sprintf_buf(sbuf_tlb_dynamic_lb, + " tlb_dynamic_lb%s %u", + !lnk->tlb_dynamic_lb_has ? "?" : "", + (int) lnk->tlb_dynamic_lb) + : ""); + + if (lnk->arp_ip_targets_num > 0) { + nm_strbuf_append_str(&buf, &len, " arp_ip_target"); + for (i = 0; i < lnk->arp_ip_targets_num; i++) { + char target[INET_ADDRSTRLEN]; + + nm_strbuf_append_c(&buf, &len, ' '); + nm_strbuf_append_str(&buf, &len, _nm_utils_inet4_ntop(lnk->arp_ip_target[i], target)); + } + } + return buf; +} + +const char * nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len) { char str_local[30]; @@ -6580,6 +6801,7 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf " src %s" "%s" /* a_acd_not_ready */ "%s" /* a_force_commit */ + "%s" /* a_no_auto_noprefixroute */ "", s_address, address->plen, @@ -6598,7 +6820,8 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf str_label, nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), address->a_acd_not_ready ? " ip4acd-not-ready" : "", - address->a_force_commit ? " force-commit" : ""); + address->a_force_commit ? " force-commit" : "", + address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); g_free(str_peer); return buf; } @@ -6719,6 +6942,7 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf len, "%s/%d lft %s pref %s%s%s%s%s src %s" "%s" /* a_force_commit */ + "%s" /* a_no_auto_noprefixroute */ "", s_address, address->plen, @@ -6729,7 +6953,8 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf str_dev, _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_force_commit ? " force-commit" : ""); + address->a_force_commit ? " force-commit" : "", + address->a_no_auto_noprefixroute ? " no-auto-noprefixroute" : ""); g_free(str_peer); return buf; } @@ -6787,6 +7012,7 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz char s_gateway[INET_ADDRSTRLEN]; char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_mss[32]; char str_table[30]; char str_scope[30]; char s_source[50]; @@ -6795,6 +7021,7 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz char str_cwnd[32]; char str_initcwnd[32]; char str_initrwnd[32]; + char str_rto_min[32]; char str_mtu[32]; char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; char str_type[30]; @@ -6821,18 +7048,20 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s%s" /* gateway */ "%s" " metric %s" - " mss %" G_GUINT32_FORMAT /* mss */ - " rt-src %s" /* protocol */ - "%s" /* rtm_flags */ - "%s%s" /* scope */ - "%s%s" /* pref-src */ - "%s" /* tos */ - "%s" /* window */ - "%s" /* cwnd */ - "%s" /* initcwnd */ - "%s" /* initrwnd */ - "%s" /* mtu */ - "%s" /* r_force_commit */ + "%s" /* mss */ + " rt-src %s" /* protocol */ + "%s" /* rtm_flags */ + "%s%s" /* scope */ + "%s%s" /* pref-src */ + "%s" /* tos */ + "%s" /* window */ + "%s" /* cwnd */ + "%s" /* initcwnd */ + "%s" /* initrwnd */ + "%s" /* rto_min */ + "%s" /* quickack */ + "%s" /* mtu */ + "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6851,7 +7080,10 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), - route->mss, + nm_sprintf_buf(str_mss, + " mss %s%" G_GUINT32_FORMAT, + route->lock_mss ? "lock " : "", + route->mss), nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)), _rtm_flags_to_string_full(str_rtm_flags, sizeof(str_rtm_flags), route->r_rtm_flags), route->scope_inv ? " scope " : "", @@ -6885,6 +7117,9 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->rto_min ? nm_sprintf_buf(str_rto_min, " rto_min %" G_GUINT32_FORMAT, route->rto_min) + : "", + route->quickack ? " quickack 1" : "", route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu, " mtu %s%" G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", @@ -6919,11 +7154,13 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz char str_pref[40]; char str_pref2[30]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_mss[32]; char s_source[50]; char str_window[32]; char str_cwnd[32]; char str_initcwnd[32]; char str_initrwnd[32]; + char str_rto_min[32]; char str_mtu[32]; char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; char str_metric[30]; @@ -6954,18 +7191,20 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s%s" /* gateway */ "%s" " metric %s" - " mss %" G_GUINT32_FORMAT /* mss */ - " rt-src %s" /* protocol */ - "%s" /* source */ - "%s" /* rtm_flags */ - "%s%s" /* pref-src */ - "%s" /* window */ - "%s" /* cwnd */ - "%s" /* initcwnd */ - "%s" /* initrwnd */ - "%s" /* mtu */ - "%s" /* pref */ - "%s" /* r_force_commit */ + "%s" /* mss */ + " rt-src %s" /* protocol */ + "%s" /* source */ + "%s" /* rtm_flags */ + "%s%s" /* pref-src */ + "%s" /* window */ + "%s" /* cwnd */ + "%s" /* initcwnd */ + "%s" /* initrwnd */ + "%s" /* rto_min */ + "%s" /* quickack */ + "%s" /* mtu */ + "%s" /* pref */ + "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), str_type), @@ -6984,7 +7223,10 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), - route->mss, + nm_sprintf_buf(str_mss, + " mss %s%" G_GUINT32_FORMAT, + route->lock_mss ? "lock " : "", + route->mss), nmp_utils_ip_config_source_to_string(route->rt_source, s_source, sizeof(s_source)), route->src_plen || !IN6_IS_ADDR_UNSPECIFIED(&route->src) ? nm_sprintf_buf(s_src_all, @@ -7017,6 +7259,9 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz route->lock_initrwnd ? "lock " : "", route->initrwnd) : "", + route->rto_min ? nm_sprintf_buf(str_rto_min, " rto_min %" G_GUINT32_FORMAT, route->rto_min) + : "", + route->quickack ? " quickack 1" : "", route->mtu || route->lock_mtu ? nm_sprintf_buf(str_mtu, " mtu %s%" G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", @@ -7492,6 +7737,107 @@ nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b) return 0; } +static NM_UTILS_FLAGS2STR_DEFINE(_mptcp_flags_to_string, + guint32, + NM_UTILS_FLAGS2STR(NM_MPTCP_PM_ADDR_FLAG_SIGNAL, "signal"), + NM_UTILS_FLAGS2STR(NM_MPTCP_PM_ADDR_FLAG_SUBFLOW, "subflow"), + NM_UTILS_FLAGS2STR(NM_MPTCP_PM_ADDR_FLAG_BACKUP, "backup"), + NM_UTILS_FLAGS2STR(NM_MPTCP_PM_ADDR_FLAG_FULLMESH, "fullmesh")); + +const char * +nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *buf, gsize len) +{ + char str_addr[30 + NM_UTILS_INET_ADDRSTRLEN]; + char str_port[30]; + char str_id[30]; + char str_flags[200]; + char str_flags2[30 + sizeof(str_flags)]; + char str_ifindex[30]; + + if (!nm_utils_to_string_buffer_init_null(mptcp_addr, &buf, &len)) + return buf; + + if (mptcp_addr->addr_family == 0) + nm_sprintf_buf(str_addr, "no-addr"); + else if (NM_IN_SET(mptcp_addr->addr_family, AF_INET, AF_INET6)) + nm_utils_inet_ntop(mptcp_addr->addr_family, &mptcp_addr->addr, str_addr); + else + nm_sprintf_buf(str_addr, "af %d", mptcp_addr->addr_family); + + if (mptcp_addr->flags != 0) + _mptcp_flags_to_string(mptcp_addr->flags, str_flags, sizeof(str_flags)); + else + str_flags[0] = '\0'; + + g_snprintf(buf, + len, + "%s" /* address */ + "%s" /* port */ + "%s" /* id */ + "%s" /* flags */ + "%s" /* ifindex */ + "", + str_addr, + mptcp_addr->port == 0 ? "" : nm_sprintf_buf(str_port, " port %u", mptcp_addr->port), + mptcp_addr->id == 0 ? "" : nm_sprintf_buf(str_id, " id %u", mptcp_addr->id), + str_flags[0] == '\0' ? "" : nm_sprintf_buf(str_flags2, " flags %s", str_flags), + mptcp_addr->ifindex == 0 + ? "" + : nm_sprintf_buf(str_ifindex, " ifindex %d", mptcp_addr->ifindex)); + return buf; +} + +void +nm_platform_mptcp_addr_hash_update(const NMPlatformMptcpAddr *obj, NMHashState *h) +{ + nm_assert(obj); + nm_assert_addr_family_or_unspec(obj->addr_family); + + nm_hash_update_vals(h, obj->id, obj->flags, obj->port, obj->addr_family, obj->ifindex); + if (NM_IN_SET(obj->addr_family, AF_INET, AF_INET6)) + nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size(obj->addr_family)); +} + +int +nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b) +{ + NM_CMP_SELF(a, b); + + nm_assert_addr_family_or_unspec(a->addr_family); + nm_assert_addr_family_or_unspec(b->addr_family); + + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, id); + NM_CMP_FIELD(a, b, addr_family); + if (NM_IN_SET(a->addr_family, AF_INET, AF_INET6)) + NM_CMP_FIELD_MEMCMP_LEN(a, b, addr, nm_utils_addr_family_to_size(a->addr_family)); + NM_CMP_FIELD(a, b, port); + + return 0; +} + +guint +nm_platform_mptcp_addr_index_addr_cmp(gconstpointer data) +{ + const NMPlatformMptcpAddr *mptcp_addr = data; + NMHashState h; + + nm_hash_init(&h, 1408914077u); + nm_hash_update_val(&h, mptcp_addr->addr_family); + nm_hash_update(&h, &mptcp_addr->addr, nm_utils_addr_family_to_size(mptcp_addr->addr_family)); + return nm_hash_complete(&h); +} + +gboolean +nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpointer data_b) +{ + const NMPlatformMptcpAddr *mptcp_addr_a = data_a; + const NMPlatformMptcpAddr *mptcp_addr_b = data_b; + + return mptcp_addr_a->addr_family == mptcp_addr_b->addr_family + && nm_ip_addr_equal(mptcp_addr_a->addr_family, &mptcp_addr_a->addr, &mptcp_addr_b->addr); +} + const char * nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len) { @@ -7668,6 +8014,95 @@ nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState * obj->vlan_stats_enabled)); } +void +nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h) +{ + nm_hash_update_vals(h, + obj->arp_all_targets, + obj->arp_interval, + obj->arp_validate, + obj->downdelay, + obj->lp_interval, + obj->miimon, + obj->min_links, + obj->packets_per_port, + obj->peer_notif_delay, + obj->primary, + obj->resend_igmp, + obj->updelay, + obj->ad_actor_sys_prio, + obj->ad_user_port_key, + obj->ad_actor_system, + obj->ad_select, + obj->all_ports_active, + obj->arp_ip_targets_num, + obj->fail_over_mac, + obj->lacp_rate, + obj->num_grat_arp, + obj->mode, + obj->primary_reselect, + obj->xmit_hash_policy, + NM_HASH_COMBINE_BOOLS(guint16, + obj->arp_all_targets_has, + obj->downdelay_has, + obj->lp_interval_has, + obj->miimon_has, + obj->peer_notif_delay_has, + obj->resend_igmp_has, + obj->tlb_dynamic_lb, + obj->tlb_dynamic_lb_has, + obj->updelay_has, + obj->use_carrier)); + + nm_hash_update(h, obj->arp_ip_target, obj->arp_ip_targets_num * sizeof(obj->arp_ip_target[0])); +} + +int +nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b) +{ + NM_CMP_SELF(a, b); + NM_CMP_FIELD_MEMCMP_LEN(a, + b, + arp_ip_target, + a->arp_ip_targets_num * sizeof(a->arp_ip_target[0])); + NM_CMP_FIELD(a, b, arp_all_targets); + NM_CMP_FIELD(a, b, arp_interval); + NM_CMP_FIELD(a, b, arp_validate); + NM_CMP_FIELD(a, b, downdelay); + NM_CMP_FIELD(a, b, lp_interval); + NM_CMP_FIELD(a, b, miimon); + NM_CMP_FIELD(a, b, min_links); + NM_CMP_FIELD(a, b, packets_per_port); + NM_CMP_FIELD(a, b, peer_notif_delay); + NM_CMP_FIELD(a, b, primary); + NM_CMP_FIELD(a, b, resend_igmp); + NM_CMP_FIELD(a, b, updelay); + NM_CMP_FIELD(a, b, ad_actor_sys_prio); + NM_CMP_FIELD(a, b, ad_user_port_key); + NM_CMP_FIELD_MEMCMP(a, b, ad_actor_system); + NM_CMP_FIELD(a, b, ad_select); + NM_CMP_FIELD(a, b, all_ports_active); + NM_CMP_FIELD(a, b, arp_ip_targets_num); + NM_CMP_FIELD(a, b, fail_over_mac); + NM_CMP_FIELD(a, b, lacp_rate); + NM_CMP_FIELD(a, b, num_grat_arp); + NM_CMP_FIELD(a, b, mode); + NM_CMP_FIELD(a, b, primary_reselect); + NM_CMP_FIELD(a, b, xmit_hash_policy); + NM_CMP_FIELD_BOOL(a, b, arp_all_targets_has); + NM_CMP_FIELD_BOOL(a, b, downdelay_has); + NM_CMP_FIELD_BOOL(a, b, lp_interval_has); + NM_CMP_FIELD_BOOL(a, b, miimon_has); + NM_CMP_FIELD_BOOL(a, b, peer_notif_delay_has); + NM_CMP_FIELD_BOOL(a, b, resend_igmp_has); + NM_CMP_FIELD_BOOL(a, b, tlb_dynamic_lb); + NM_CMP_FIELD_BOOL(a, b, tlb_dynamic_lb_has); + NM_CMP_FIELD_BOOL(a, b, updelay_has); + NM_CMP_FIELD_BOOL(a, b, use_carrier); + + return 0; +} + int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b) { @@ -8182,7 +8617,8 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState NM_HASH_COMBINE_BOOLS(guint8, obj->use_ip4_broadcast_address, obj->a_acd_not_ready, - obj->a_force_commit)); + obj->a_force_commit, + obj->a_no_auto_noprefixroute)); nm_hash_update_strarr(h, obj->label); } @@ -8231,6 +8667,7 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, NM_CMP_FIELD(a, b, broadcast_address); NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); } return 0; } @@ -8240,17 +8677,18 @@ nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) { - nm_hash_update_vals(h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); + nm_hash_update_vals( + h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit, obj->a_no_auto_noprefixroute)); } int @@ -8294,6 +8732,7 @@ nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, NM_CMP_FIELD(a, b, n_ifa_flags); NM_CMP_FIELD(a, b, addr_source); NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + NM_CMP_FIELD_UNSAFE(a, b, a_no_auto_noprefixroute); } return 0; } @@ -8337,15 +8776,18 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->initcwnd, obj->initrwnd, obj->mtu, + obj->rto_min, obj->r_rtm_flags & RTNH_F_ONLINK, - NM_HASH_COMBINE_BOOLS(guint8, + NM_HASH_COMBINE_BOOLS(guint16, obj->metric_any, obj->table_any, + obj->quickack, obj->lock_window, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, - obj->lock_mtu)); + obj->lock_mtu, + obj->lock_mss)); break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: nm_hash_update_vals( @@ -8367,15 +8809,18 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->initcwnd, obj->initrwnd, obj->mtu, + obj->rto_min, obj->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK), - NM_HASH_COMBINE_BOOLS(guint8, + NM_HASH_COMBINE_BOOLS(guint16, obj->metric_any, obj->table_any, + obj->quickack, obj->lock_window, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, - obj->lock_mtu)); + obj->lock_mtu, + obj->lock_mss)); break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: nm_hash_update_vals(h, @@ -8396,15 +8841,18 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->initcwnd, obj->initrwnd, obj->mtu, + obj->rto_min, obj->r_rtm_flags, - NM_HASH_COMBINE_BOOLS(guint8, + NM_HASH_COMBINE_BOOLS(guint16, obj->metric_any, obj->table_any, + obj->quickack, obj->lock_window, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, + obj->lock_mss, obj->r_force_commit)); break; } @@ -8442,12 +8890,15 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); + NM_CMP_FIELD(a, b, rto_min); NM_CMP_DIRECT(a->r_rtm_flags & RTNH_F_ONLINK, b->r_rtm_flags & RTNH_F_ONLINK); + NM_CMP_FIELD_UNSAFE(a, b, quickack); NM_CMP_FIELD_UNSAFE(a, b, lock_window); NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); + NM_CMP_FIELD_UNSAFE(a, b, lock_mss); } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: @@ -8485,16 +8936,19 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, } else NM_CMP_FIELD(a, b, r_rtm_flags); NM_CMP_FIELD(a, b, tos); + NM_CMP_FIELD_UNSAFE(a, b, quickack); NM_CMP_FIELD_UNSAFE(a, b, lock_window); NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); + NM_CMP_FIELD_UNSAFE(a, b, lock_mss); NM_CMP_FIELD(a, b, window); NM_CMP_FIELD(a, b, cwnd); NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); + NM_CMP_FIELD(a, b, rto_min); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); break; @@ -8552,19 +9006,22 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), obj->mss, obj->r_rtm_flags & RTM_F_CLONED, - NM_HASH_COMBINE_BOOLS(guint8, + NM_HASH_COMBINE_BOOLS(guint16, obj->metric_any, obj->table_any, + obj->quickack, obj->lock_window, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, - obj->lock_mtu), + obj->lock_mtu, + obj->lock_mss), obj->window, obj->cwnd, obj->initcwnd, obj->initrwnd, obj->mtu, + obj->rto_min, _route_pref_normalize(obj->rt_pref)); break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: @@ -8584,17 +9041,20 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, NM_HASH_COMBINE_BOOLS(guint16, obj->metric_any, obj->table_any, + obj->quickack, obj->lock_window, obj->lock_cwnd, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, + obj->lock_mss, obj->r_force_commit), obj->window, obj->cwnd, obj->initcwnd, obj->initrwnd, obj->mtu, + obj->rto_min, obj->rt_pref); break; } @@ -8658,16 +9118,19 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_DIRECT(a->r_rtm_flags & RTM_F_CLONED, b->r_rtm_flags & RTM_F_CLONED); } else NM_CMP_FIELD(a, b, r_rtm_flags); + NM_CMP_FIELD_UNSAFE(a, b, quickack); NM_CMP_FIELD_UNSAFE(a, b, lock_window); NM_CMP_FIELD_UNSAFE(a, b, lock_cwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initcwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); + NM_CMP_FIELD_UNSAFE(a, b, lock_mss); NM_CMP_FIELD(a, b, window); NM_CMP_FIELD(a, b, cwnd); NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); + NM_CMP_FIELD(a, b, rto_min); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); else @@ -8927,6 +9390,37 @@ 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); +} + +/*****************************************************************************/ + +int +nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr) +{ + _CHECK_SELF(self, klass, -NME_BUG); + + return klass->mptcp_addr_update(self, add, addr); +} + +GPtrArray * +nm_platform_mptcp_addrs_dump(NMPlatform *self) +{ + _CHECK_SELF(self, klass, NULL); + + return klass->mptcp_addrs_dump(self); +} + +/*****************************************************************************/ + GHashTable * nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex) { @@ -8939,7 +9433,7 @@ nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex) g_return_val_if_fail(NM_IS_PLATFORM(self), NULL); g_return_val_if_fail(ifindex > 0, NULL); - nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex); head_entry = nmp_cache_lookup(NM_PLATFORM_GET_PRIVATE(self)->cache, &lookup); @@ -8975,8 +9469,6 @@ nm_platform_ip4_address_generate_device_route(const NMPlatformIP4Address *addr, nm_assert(addr); nm_assert(addr->plen <= 32); - if (addr->plen == 0) - return NULL; if (addr->plen == 0) return NULL; @@ -9037,9 +9529,13 @@ log_link(NMPlatform *self, { char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - _LOG3D("signal: link %7s: %s", - nm_platform_signal_change_type_to_string(change_type), - nm_platform_link_to_string(device, sbuf, sizeof(sbuf))); + if (_LOGD_ENABLED()) { + NMLOG_COMMON(LOGL_DEBUG, + device->name, + "signal: link %7s: %s", + nm_platform_signal_change_type_to_string(change_type), + nm_platform_link_to_string(device, sbuf, sizeof(sbuf))); + } } static void diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 190a91dc..90ffbed0 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -61,6 +61,12 @@ typedef gboolean (*NMPObjectPredicateFunc)(const NMPObject *obj, gpointer user_d #define NM_IFF_MULTI_QUEUE 0x0100 /* IFF_MULTI_QUEUE */ +#define NM_MPTCP_PM_ADDR_FLAG_SIGNAL ((guint32) (1 << 0)) +#define NM_MPTCP_PM_ADDR_FLAG_SUBFLOW ((guint32) (1 << 1)) +#define NM_MPTCP_PM_ADDR_FLAG_BACKUP ((guint32) (1 << 2)) +#define NM_MPTCP_PM_ADDR_FLAG_FULLMESH ((guint32) (1 << 3)) +#define NM_MPTCP_PM_ADDR_FLAG_IMPLICIT ((guint32) (1 << 4)) + /* Redefine this in host's endianness */ #define NM_GRE_KEY 0x2000 @@ -333,6 +339,12 @@ typedef enum { * should be configured. */ \ bool a_force_commit : 1; \ \ + /* nm_platform_ip_address_sync() likes to add IFA_F_NOPREFIXROUTE flag for all + * addresses, regardless of a_ifi_flags property. By setting this boolean, that + * automatism can be suppressed, and the noprefixroute flag does not get added + * automatically. */ \ + bool a_no_auto_noprefixroute : 1; \ + \ /* Don't have a bitfield as last field in __NMPlatformIPAddress_COMMON. It would then * be unclear how the following fields get merged. We could also use a zero bitfield, * but instead we just have there the uint8 field. */ \ @@ -453,6 +465,9 @@ typedef union { /* RTA_METRICS.RTAX_INITRWND (iproute2: initrwnd) */ \ guint32 initrwnd; \ \ + /* RTA_METRICS.RTAX_RTO_MIN (iproute2: rto_min) */ \ + guint32 rto_min; \ + \ /* RTA_METRICS.RTAX_MTU (iproute2: mtu) */ \ guint32 mtu; \ \ @@ -499,6 +514,10 @@ typedef union { bool lock_initcwnd : 1; \ bool lock_initrwnd : 1; \ bool lock_mtu : 1; \ + bool lock_mss : 1; \ + \ + /* RTA_METRICS.RTAX_QUICKACK (iproute2: quickack) */ \ + bool quickack : 1; \ \ /* if TRUE, the "metric" field is interpreted as an offset that is added to a default * metric. For example, form a DHCP lease we don't know the actually used metric, because @@ -775,8 +794,6 @@ typedef struct { NMPlatformAction action; } NMPlatformTfilter; -#undef __NMPlatformObjWithIfindex_COMMON - typedef struct { bool is_ip4; NMPObjectType obj_type; @@ -826,10 +843,10 @@ typedef struct { } NMPlatformVFVlan; typedef struct { + guint num_vlans; guint32 index; guint32 min_tx_rate; guint32 max_tx_rate; - guint num_vlans; NMPlatformVFVlan *vlans; struct { guint8 data[20]; /* _NM_UTILS_HWADDR_LEN_MAX */ @@ -847,41 +864,82 @@ typedef struct { } NMPlatformBridgeVlan; typedef struct { - NMEtherAddr group_addr; - bool mcast_querier : 1; - bool mcast_query_use_ifaddr : 1; - bool mcast_snooping : 1; - bool stp_state : 1; - bool vlan_stats_enabled : 1; - guint16 group_fwd_mask; - guint16 priority; - guint16 vlan_protocol; - guint32 ageing_time; - guint32 forward_delay; - guint32 hello_time; - guint32 max_age; - guint32 mcast_last_member_count; - guint32 mcast_startup_query_count; - guint32 mcast_hash_max; guint64 mcast_last_member_interval; guint64 mcast_membership_interval; guint64 mcast_querier_interval; guint64 mcast_query_interval; guint64 mcast_query_response_interval; guint64 mcast_startup_query_interval; + guint32 ageing_time; + guint32 forward_delay; + guint32 hello_time; + guint32 max_age; + guint32 mcast_hash_max; + guint32 mcast_last_member_count; + guint32 mcast_startup_query_count; + guint16 group_fwd_mask; + guint16 priority; + guint16 vlan_protocol; + NMEtherAddr group_addr; guint8 mcast_router; + bool mcast_querier : 1; + bool mcast_query_use_ifaddr : 1; + bool mcast_snooping : 1; + bool stp_state : 1; + bool vlan_stats_enabled : 1; } NMPlatformLnkBridge; extern const NMPlatformLnkBridge nm_platform_lnk_bridge_default; +/* Defined in net/bonding.h. */ +#define NM_BOND_MAX_ARP_TARGETS 16 + typedef struct { + in_addr_t arp_ip_target[NM_BOND_MAX_ARP_TARGETS]; + guint32 arp_all_targets; + guint32 arp_interval; + guint32 arp_validate; + guint32 downdelay; + guint32 lp_interval; + guint32 miimon; + guint32 min_links; + guint32 packets_per_port; + guint32 peer_notif_delay; + guint32 primary; + guint32 resend_igmp; + guint32 updelay; + guint16 ad_actor_sys_prio; + guint16 ad_user_port_key; + NMEtherAddr ad_actor_system; + guint8 ad_select; + guint8 all_ports_active; + guint8 arp_ip_targets_num; + guint8 fail_over_mac; + guint8 lacp_rate; + guint8 num_grat_arp; + guint8 mode; + guint8 primary_reselect; + guint8 xmit_hash_policy; + bool arp_all_targets_has : 1; + bool downdelay_has : 1; + bool lp_interval_has : 1; + bool miimon_has : 1; + bool peer_notif_delay_has : 1; + bool resend_igmp_has : 1; + bool tlb_dynamic_lb : 1; + bool tlb_dynamic_lb_has : 1; + bool updelay_has : 1; + bool use_carrier : 1; +} NMPlatformLnkBond; + +typedef struct { + int parent_ifindex; in_addr_t local; in_addr_t remote; - int parent_ifindex; - guint16 input_flags; - guint16 output_flags; guint32 input_key; guint32 output_key; + guint16 input_flags; + guint16 output_flags; guint8 ttl; guint8 tos; bool path_mtu_discovery : 1; @@ -897,12 +955,12 @@ typedef struct { struct in6_addr local; struct in6_addr remote; int parent_ifindex; + guint flow_label; + guint32 flags; guint8 ttl; guint8 tclass; guint8 encap_limit; guint8 proto; - guint flow_label; - guint32 flags; /* IP6GRE only */ guint32 input_key; @@ -914,9 +972,9 @@ typedef struct { } NMPlatformLnkIp6Tnl; typedef struct { + int parent_ifindex; in_addr_t local; in_addr_t remote; - int parent_ifindex; guint8 ttl; guint8 tos; bool path_mtu_discovery : 1; @@ -945,9 +1003,9 @@ typedef struct { } NMPlatformLnkMacvlan; typedef struct { + int parent_ifindex; in_addr_t local; in_addr_t remote; - int parent_ifindex; guint16 flags; guint8 ttl; guint8 tos; @@ -983,9 +1041,9 @@ typedef struct { typedef struct { struct in6_addr group6; struct in6_addr local6; + int parent_ifindex; in_addr_t group; in_addr_t local; - int parent_ifindex; guint32 id; guint32 ageing; guint32 limit; @@ -1038,6 +1096,17 @@ typedef enum { typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data); +typedef struct { + __NMPlatformObjWithIfindex_COMMON; + guint32 id; + guint32 flags; + guint16 port; + NMIPAddr addr; + gint8 addr_family; +} NMPlatformMptcpAddr; + +#undef __NMPlatformObjWithIfindex_COMMON + /*****************************************************************************/ typedef struct _NMPlatformCsmeConnInfo { @@ -1096,6 +1165,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; @@ -1205,6 +1293,7 @@ typedef struct { gboolean egress_reset_all, const NMVlanQosMapping *egress_map, gsize n_egress_map); + gboolean (*link_tun_add)(NMPlatform *self, const char *name, const NMPlatformLnkTun *props, @@ -1302,6 +1391,13 @@ 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); + + int (*mptcp_addr_update)(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr); + + GPtrArray *(*mptcp_addrs_dump)(NMPlatform *self); + } NMPlatformClass; /* NMPlatform signals @@ -1489,6 +1585,49 @@ nm_platform_route_type_uncoerce(guint8 type_coerced) return nm_platform_route_type_coerce(type_coerced); } +static inline guint8 +nm_platform_ip4_address_get_scope(in_addr_t addr) +{ + /* For IPv4 addresses, we can set any scope we want (for any address). + * However, there are scopes that make sense based on the address, + * so choose those. */ + return nm_utils_ip4_address_is_loopback(addr) ? (254 /* RT_SCOPE_HOST */) + : nm_utils_ip4_address_is_link_local(addr) ? (253 /* RT_SCOPE_LINK */) + : (0 /* RT_SCOPE_UNIVERSE */); +} + +static inline guint8 +nm_platform_ip6_address_get_scope(const struct in6_addr *addr) +{ + /* For IPv6, kernel does not allow userspace to configure the address scope. + * Instead, it is calculated based on the address. See rt_scope() and + * ipv6_addr_scope(). We do the same here. */ + return IN6_IS_ADDR_LOOPBACK(addr) ? (254 /* RT_SCOPE_HOST */) + : IN6_IS_ADDR_LINKLOCAL(addr) ? (253 /* RT_SCOPE_LINK */) + : IN6_IS_ADDR_SITELOCAL(addr) ? (200 /* RT_SCOPE_SITE */) + : (0 /* RT_SCOPE_UNIVERSE */); +} + +static inline guint8 +nm_platform_ip_address_get_scope(int addr_family, gconstpointer addr) +{ + /* Note that this function returns the scope as we configure + * it in kernel (for IPv4) or as kernel chooses it (for IPv6). + * + * That means, rfc1918 private addresses nm_utils_ip_is_site_local() are + * considered RT_SCOPE_UNIVERSE. + * + * Also, the deprecated IN6_IS_ADDR_SITELOCAL() addresses (fec0::/10) + * are considered RT_SCOPE_SITE, while unique local addresses (ULA, fc00::/7) + * are considered RT_SCOPE_UNIVERSE. + * + * You may not want to use this function when reasoning about + * site-local addresses (RFC1918, ULA). */ + if (NM_IS_IPv4(addr_family)) + return nm_platform_ip4_address_get_scope(*((in_addr_t *) addr)); + return nm_platform_ip6_address_get_scope(addr); +} + gboolean nm_platform_get_use_udev(NMPlatform *self); gboolean nm_platform_get_log_with_ptr(NMPlatform *self); gboolean nm_platform_get_cache_tc(NMPlatform *self); @@ -1657,9 +1796,18 @@ nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLn } static inline int -nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) +nm_platform_link_bond_change(NMPlatform *self, int ifindex, const NMPlatformLnkBond *props) { - return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link); + return nm_platform_link_change(self, NM_LINK_TYPE_BOND, ifindex, props); +} + +static inline int +nm_platform_link_bond_add(NMPlatform *self, + const char *name, + const NMPlatformLnkBond *props, + const NMPlatformLink **out_link) +{ + return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, props, out_link); } static inline int @@ -1993,6 +2141,8 @@ const NMPObject *nm_platform_link_get_lnk(NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); +const NMPlatformLnkBond * +nm_platform_link_get_lnk_bond(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkBridge * nm_platform_link_get_lnk_bridge(NMPlatform *self, int ifindex, const NMPlatformLink **out_link); const NMPlatformLnkGre * @@ -2287,6 +2437,7 @@ gboolean nm_platform_tc_sync(NMPlatform *self, GPtrArray *known_tfilters); const char *nm_platform_link_to_string(const NMPlatformLink *link, char *buf, gsize len); +const char *nm_platform_lnk_bond_to_string(const NMPlatformLnkBond *lnk, char *buf, gsize len); const char *nm_platform_lnk_bridge_to_string(const NMPlatformLnkBridge *lnk, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string(const NMPlatformLnkGre *lnk, char *buf, gsize len); const char * @@ -2326,7 +2477,11 @@ const char *nm_platform_vlan_qos_mapping_to_string(const char *name, const char * nm_platform_wireguard_peer_to_string(const struct _NMPWireGuardPeer *peer, char *buf, gsize len); +const char * +nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *buf, gsize len); + int nm_platform_link_cmp(const NMPlatformLink *a, const NMPlatformLink *b); +int nm_platform_lnk_bond_cmp(const NMPlatformLnkBond *a, const NMPlatformLnkBond *b); int nm_platform_lnk_bridge_cmp(const NMPlatformLnkBridge *a, const NMPlatformLnkBridge *b); int nm_platform_lnk_gre_cmp(const NMPlatformLnkGre *a, const NMPlatformLnkGre *b); int nm_platform_lnk_infiniband_cmp(const NMPlatformLnkInfiniband *a, @@ -2404,6 +2559,8 @@ int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, gboolean compare_handle); int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b); +int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b); + void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); void nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState *h); void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h); @@ -2416,6 +2573,7 @@ void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, void nm_platform_routing_rule_hash_update(const NMPlatformRoutingRule *obj, NMPlatformRoutingRuleCmpType cmp_type, NMHashState *h); +void nm_platform_lnk_bond_hash_update(const NMPlatformLnkBond *obj, NMHashState *h); void nm_platform_lnk_bridge_hash_update(const NMPlatformLnkBridge *obj, NMHashState *h); void nm_platform_lnk_gre_hash_update(const NMPlatformLnkGre *obj, NMHashState *h); void nm_platform_lnk_infiniband_hash_update(const NMPlatformLnkInfiniband *obj, NMHashState *h); @@ -2433,6 +2591,11 @@ void nm_platform_lnk_wireguard_hash_update(const NMPlatformLnkWireGuard *obj, NM void nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h); void nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *h); +void nm_platform_mptcp_addr_hash_update(const NMPlatformMptcpAddr *obj, NMHashState *h); + +guint nm_platform_mptcp_addr_index_addr_cmp(gconstpointer data); +gboolean nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpointer data_b); + #define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len); @@ -2505,4 +2668,13 @@ 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); + +int +nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr); + +GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self); + #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index 48631688..a6ee3df1 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -147,6 +147,9 @@ typedef enum _nm_packed { NMP_OBJECT_TYPE_LNK_VRF, NMP_OBJECT_TYPE_LNK_VXLAN, NMP_OBJECT_TYPE_LNK_WIREGUARD, + NMP_OBJECT_TYPE_LNK_BOND, + + NMP_OBJECT_TYPE_MPTCP_ADDR, __NMP_OBJECT_TYPE_LAST, NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1, diff --git a/src/libnm-platform/nmp-global-tracker.c b/src/libnm-platform/nmp-global-tracker.c new file mode 100644 index 00000000..ea4da284 --- /dev/null +++ b/src/libnm-platform/nmp-global-tracker.c @@ -0,0 +1,1261 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" + +#include "nmp-global-tracker.h" + +#include <linux/fib_rules.h> +#include <linux/rtnetlink.h> + +#include "libnm-log-core/nm-logging.h" +#include "libnm-std-aux/c-list-util.h" +#include "nmp-object.h" + +/* This limit comes from kernel, and it limits the number of MPTCP addresses + * we can configure. */ +#define MPTCP_PM_ADDR_MAX 8 + +/*****************************************************************************/ + +/* NMPGlobalTracker tracks certain objects for the entire network namespace and can + * commit them. + * + * We tend to configure things per-interface and per-profile. In many cases, + * we thereby only need to care about the things for that interface. For example, + * we can configure IP addresses and (unicast) routes without having a system wide + * view. That is mainly, because such objects are themselves tied to an ifindex. + * + * However, for certain objects that's not the case. For example, policy routing + * rules, certain route types (blackhole, unavailable, prohibit, throw) and MPTCP + * endpoints require a holistic view of the system. That is, because rules and + * these route types have no ifindex. For MPTCP endpoints, they have an ifindex, + * however we can only configure a small number of them at a time, so we need a + * central (global) instance that can track which endpoints to configure. + * + * In general, the NMPGlobalTracker tracks objects for the entire namespace, and + * it's sync() method will figure out how to configure them. + * + * Since the users of NMPGloablTracker (NML3Cfg, NMDevice) themselves don't + * have this holistic view, the API of NMPGlobalTracker allows them to track + * individual objects independently (they register their objects for a private + * user-tag). If multiple such independent users track the same object, the tracking + * priority (track_priority_val) determines which one wins. + * + * NMPGlobalTracker can not only track whether an object should be present, + * it also can track whether it should be absent. See track_priority_present. + */ + +/*****************************************************************************/ + +struct _NMPGlobalTracker { + NMPlatform *platform; + GHashTable *by_obj; + GHashTable *by_user_tag; + GHashTable *by_data; + CList by_obj_lst_heads[4]; + guint ref_count; +}; + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG_PREFIX_NAME "global-tracker" + +#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, LOGD_PLATFORM, _NMLOG_PREFIX_NAME, __VA_ARGS__) + +/*****************************************************************************/ + +static gboolean +NMP_IS_GLOBAL_TRACKER(gpointer self) +{ + return self && ((NMPGlobalTracker *) self)->ref_count > 0 + && NM_IS_PLATFORM(((NMPGlobalTracker *) self)->platform); +} + +/*****************************************************************************/ + +typedef struct { + const NMPObject *obj; + gconstpointer user_tag; + CList obj_lst; + CList user_tag_lst; + + /* @track_priority_val zero is special: those are weakly tracked objects. + * That means: NetworkManager will restore them only if it removed them earlier. + * But it will not remove or add them otherwise. + * + * Otherwise, @track_priority_val goes together with @track_priority_present. + * In case of one object being tracked multiple times (with different priorities), + * the one with higher priority wins. See _track_obj_data_get_best_data(). + * Then, the winning present state either enforces that the rule is present + * or absent. + * + * If an object is not tracked at all, it is ignored by NetworkManager (except + * for MPTCP endpoints for the tracked interface). Assuming that it was added + * externally by the user. But unlike weakly tracked rules, NM will *not* restore + * such rules if NetworkManager themself removed them. */ + guint32 track_priority_val; + bool track_priority_present : 1; + + /* Calling nmp_global_tracker_track() will ensure that the tracked entry is + * non-dirty. Together with nmp_global_tracker_set_dirty() and nmp_global_tracker_untrack_all()'s + * @all parameter, this can be used to remove stale entries. */ + bool dirty : 1; +} TrackData; + +typedef enum { + CONFIG_STATE_NONE = 0, + CONFIG_STATE_ADDED_BY_US = 1, + CONFIG_STATE_REMOVED_BY_US = 2, + + /* ConfigState encodes whether the object was touched by us at all (CONFIG_STATE_NONE). + * + * Maybe we would only need to track whether we touched the object at all. But we + * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US) + * or did we remove it (CONFIG_STATE_REMOVED_BY_US)? + * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively + * add/remove it, but whenever we are about to undo the add/remove, we need to do it. + * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally + * force the state next time when necessary. */ + CONFIG_STATE_OWNED_BY_US = 3, +} ConfigState; + +typedef struct { + const NMPObject *obj; + CList obj_lst_head; + + CList by_obj_lst; + + /* indicates whether we configured/removed the object (during sync()). We need that, so + * if the object gets untracked, that we know to remove/restore it. + * + * This makes NMPGlobalTracker stateful (beyond the configuration that indicates + * which objects are tracked). + * After a restart, NetworkManager would no longer remember which objects were added + * by us. + * + * That is partially fixed by NetworkManager taking over the objects that it + * actively configures (see %NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG). */ + ConfigState config_state; +} TrackObjData; + +typedef struct { + gconstpointer user_tag; + CList user_tag_lst_head; +} TrackUserTagData; + +/*****************************************************************************/ + +static void _track_data_untrack(NMPGlobalTracker *self, + TrackData *track_data, + gboolean remove_user_tag_data, + gboolean make_owned_by_us); + +/*****************************************************************************/ + +static CList * +_by_obj_lst_head(NMPGlobalTracker *self, NMPObjectType obj_type) +{ + G_STATIC_ASSERT(G_N_ELEMENTS(self->by_obj_lst_heads) == 4); + + switch (obj_type) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + return &self->by_obj_lst_heads[0]; + case NMP_OBJECT_TYPE_IP6_ROUTE: + return &self->by_obj_lst_heads[1]; + case NMP_OBJECT_TYPE_ROUTING_RULE: + return &self->by_obj_lst_heads[2]; + case NMP_OBJECT_TYPE_MPTCP_ADDR: + return &self->by_obj_lst_heads[3]; + default: + return nm_assert_unreachable_val(NULL); + } +} + +/*****************************************************************************/ + +static void +_track_data_assert(const TrackData *track_data, gboolean linked) +{ + nm_assert(track_data); + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(track_data->obj), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, + NMP_OBJECT_TYPE_MPTCP_ADDR)); + nm_assert(nmp_object_is_visible(track_data->obj)); + nm_assert(track_data->user_tag); + nm_assert(!linked || !c_list_is_empty(&track_data->obj_lst)); + nm_assert(!linked || !c_list_is_empty(&track_data->user_tag_lst)); +} + +static guint +_track_data_hash(gconstpointer data) +{ + const TrackData *track_data = data; + NMHashState h; + + _track_data_assert(track_data, FALSE); + + nm_hash_init(&h, 269297543u); + nmp_object_id_hash_update(track_data->obj, &h); + nm_hash_update_val(&h, track_data->user_tag); + return nm_hash_complete(&h); +} + +static gboolean +_track_data_equal(gconstpointer data_a, gconstpointer data_b) +{ + const TrackData *track_data_a = data_a; + const TrackData *track_data_b = data_b; + + _track_data_assert(track_data_a, FALSE); + _track_data_assert(track_data_b, FALSE); + + return track_data_a->user_tag == track_data_b->user_tag + && nmp_object_id_equal(track_data_a->obj, track_data_b->obj); +} + +static void +_track_data_destroy(gpointer data) +{ + TrackData *track_data = data; + + _track_data_assert(track_data, FALSE); + + c_list_unlink_stale(&track_data->obj_lst); + c_list_unlink_stale(&track_data->user_tag_lst); + nmp_object_unref(track_data->obj); + nm_g_slice_free(track_data); +} + +static const TrackData * +_track_obj_data_get_best_data(TrackObjData *obj_data) +{ + TrackData *track_data; + const TrackData *td_best = NULL; + + c_list_for_each_entry (track_data, &obj_data->obj_lst_head, obj_lst) { + _track_data_assert(track_data, TRUE); + + if (td_best) { + if (td_best->track_priority_val > track_data->track_priority_val) + continue; + if (td_best->track_priority_val == track_data->track_priority_val) { + if (td_best->track_priority_present || !track_data->track_priority_present) { + /* if the priorities are identical, then "present" wins over + * "!present" (absent). */ + continue; + } + } + } + + td_best = track_data; + } + + return td_best; +} + +static guint +_track_obj_data_hash(gconstpointer data) +{ + const TrackObjData *obj_data = data; + + return nmp_object_id_hash(obj_data->obj); +} + +static gboolean +_track_obj_data_equal(gconstpointer data_a, gconstpointer data_b) +{ + const TrackObjData *obj_data_a = data_a; + const TrackObjData *obj_data_b = data_b; + + return nmp_object_id_equal(obj_data_a->obj, obj_data_b->obj); +} + +static void +_track_obj_data_destroy(gpointer data) +{ + TrackObjData *obj_data = data; + + c_list_unlink_stale(&obj_data->obj_lst_head); + c_list_unlink_stale(&obj_data->by_obj_lst); + nmp_object_unref(obj_data->obj); + nm_g_slice_free(obj_data); +} + +static void +_track_user_tag_data_destroy(gpointer data) +{ + TrackUserTagData *user_tag_data = data; + + c_list_unlink_stale(&user_tag_data->user_tag_lst_head); + nm_g_slice_free(user_tag_data); +} + +static TrackData * +_track_data_lookup(GHashTable *by_data, const NMPObject *obj, gconstpointer user_tag) +{ + TrackData track_data_needle = { + .obj = obj, + .user_tag = user_tag, + }; + + return g_hash_table_lookup(by_data, &track_data_needle); +} + +/*****************************************************************************/ + +static const NMPObject * +_obj_stackinit(NMPObject *obj_stack, NMPObjectType obj_type, gconstpointer obj) +{ + nmp_object_stackinit(obj_stack, obj_type, obj); + + if (NM_MORE_ASSERTS > 10) { + if (obj_type == NMP_OBJECT_TYPE_MPTCP_ADDR) { + NMPlatformMptcpAddr *m = NMP_OBJECT_CAST_MPTCP_ADDR(obj_stack); + NMPlatformMptcpAddr m_dummy; + + /* Only certain MPTCP addresses can be added. */ + nm_assert(m->ifindex > 0); + if (nm_platform_mptcp_addr_cmp( + nmp_global_tracker_mptcp_addr_init_for_ifindex(&m_dummy, m->ifindex), + m) + == 0) { + /* This is a dummy instance. We are good. */ + } else { + nm_assert_addr_family(m->addr_family); + nm_assert(m->port == 0); + nm_assert(m->id == 0); + } + } + } + + nm_assert(nmp_object_is_visible(obj_stack)); + return obj_stack; +} + +/** + * nmp_global_tracker_track: + * @self: the #NMPGlobalTracker instance + * @obj_type: the NMPObjectType of @obj that we are tracking. + * @obj: the NMPlatformObject (of type NMPObjectType) to track. Usually + * a #NMPlatformRoutingRule, #NMPlatformIP4Route, #NMPlatformIP6Route + * or #NMPlatformMptcpAddr pointer. + * @track_priority: the priority for tracking the rule. Note that + * negative values indicate a forced absence of the rule. Priorities + * are compared with their absolute values (with higher absolute + * value being more important). For example, if you track the same + * rule twice, once with priority -5 and +10, then the rule is + * present (because the positive number is more important). + * The special value 0 indicates weakly-tracked rules. + * @user_tag: the tag associated with tracking this rule. The same tag + * must be used to untrack the rule later. + * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag + * for the same rule. Note that this is different from a plain nmp_global_tracker_untrack_rule(), + * because it enforces ownership of the now tracked rule. On the other hand, + * a plain nmp_global_tracker_untrack_rule() merely forgets about the tracking. + * The purpose here is to set this to %NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG. + * + * Returns: %TRUE, if something changed. + */ +gboolean +nmp_global_tracker_track(NMPGlobalTracker *self, + NMPObjectType obj_type, + gconstpointer obj, + gint32 track_priority, + gconstpointer user_tag, + gconstpointer user_tag_untrack) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + TrackData *track_data; + TrackObjData *obj_data; + TrackUserTagData *user_tag_data; + gboolean changed = FALSE; + gboolean changed_untrack = FALSE; + guint32 track_priority_val; + gboolean track_priority_present; + + g_return_val_if_fail(NMP_IS_GLOBAL_TRACKER(self), FALSE); + g_return_val_if_fail(obj, FALSE); + g_return_val_if_fail(user_tag, FALSE); + + /* The route must not be tied to an interface. We can only handle here + * blackhole/unreachable/prohibit route types. */ + g_return_val_if_fail( + NM_IN_SET(obj_type, NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_MPTCP_ADDR) + || (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) + && ((const NMPlatformIPRoute *) obj)->ifindex == 0), + FALSE); + + /* only positive track priorities are implemented for MPTCP addrs. */ + nm_assert(obj_type != NMP_OBJECT_TYPE_MPTCP_ADDR || track_priority > 0); + + p_obj_stack = _obj_stackinit(&obj_stack, obj_type, obj); + + if (track_priority >= 0) { + track_priority_val = track_priority; + track_priority_present = TRUE; + } else { + track_priority_val = -track_priority; + track_priority_present = FALSE; + } + + track_data = _track_data_lookup(self->by_data, p_obj_stack, user_tag); + + if (!track_data) { + track_data = g_slice_new(TrackData); + *track_data = (TrackData){ + .obj = nm_dedup_multi_index_obj_intern(nm_platform_get_multi_idx(self->platform), + p_obj_stack), + .user_tag = user_tag, + .track_priority_val = track_priority_val, + .track_priority_present = track_priority_present, + .dirty = FALSE, + }; + g_hash_table_add(self->by_data, track_data); + + obj_data = g_hash_table_lookup(self->by_obj, &track_data->obj); + if (!obj_data) { + obj_data = g_slice_new(TrackObjData); + *obj_data = (TrackObjData){ + .obj = nmp_object_ref(track_data->obj), + .obj_lst_head = C_LIST_INIT(obj_data->obj_lst_head), + .config_state = CONFIG_STATE_NONE, + }; + g_hash_table_add(self->by_obj, obj_data); + c_list_link_tail(_by_obj_lst_head(self, obj_type), &obj_data->by_obj_lst); + } + c_list_link_tail(&obj_data->obj_lst_head, &track_data->obj_lst); + + user_tag_data = g_hash_table_lookup(self->by_user_tag, &track_data->user_tag); + if (!user_tag_data) { + user_tag_data = g_slice_new(TrackUserTagData); + *user_tag_data = (TrackUserTagData){ + .user_tag = user_tag, + .user_tag_lst_head = C_LIST_INIT(user_tag_data->user_tag_lst_head), + }; + g_hash_table_add(self->by_user_tag, user_tag_data); + } + c_list_link_tail(&user_tag_data->user_tag_lst_head, &track_data->user_tag_lst); + changed = TRUE; + } else { + track_data->dirty = FALSE; + if (track_data->track_priority_val != track_priority_val + || track_data->track_priority_present != track_priority_present) { + track_data->track_priority_val = track_priority_val; + track_data->track_priority_present = track_priority_present; + changed = TRUE; + } + } + + if (user_tag_untrack) { + if (user_tag != user_tag_untrack) { + TrackData *track_data_untrack; + + track_data_untrack = _track_data_lookup(self->by_data, p_obj_stack, user_tag_untrack); + if (track_data_untrack) { + _track_data_untrack(self, track_data_untrack, FALSE, TRUE); + changed_untrack = TRUE; + } + } else + nm_assert_not_reached(); + } + + _track_data_assert(track_data, TRUE); + + if (changed) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + _LOGD( + "track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", + NM_HASH_OBFUSCATE_PTR(track_data->user_tag), + (track_data->track_priority_val == 0 + ? "" + : (track_data->track_priority_present ? "+" : "-")), + (guint) track_data->track_priority_val, + NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + } + + return changed || changed_untrack; +} + +static void +_track_data_untrack(NMPGlobalTracker *self, + TrackData *track_data, + gboolean remove_user_tag_data, + gboolean make_owned_by_us) +{ + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + TrackObjData *obj_data; + + nm_assert(NMP_IS_GLOBAL_TRACKER(self)); + _track_data_assert(track_data, TRUE); + nm_assert(self->by_data); + nm_assert(g_hash_table_lookup(self->by_data, track_data) == track_data); + + _LOGD("untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] %s \"%s\"", + NM_HASH_OBFUSCATE_PTR(track_data->user_tag), + NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + +#if NM_MORE_ASSERTS + { + TrackUserTagData *user_tag_data; + + user_tag_data = g_hash_table_lookup(self->by_user_tag, &track_data->user_tag); + nm_assert(user_tag_data); + nm_assert(c_list_contains(&user_tag_data->user_tag_lst_head, &track_data->user_tag_lst)); + } +#endif + + nm_assert(!c_list_is_empty(&track_data->user_tag_lst)); + + obj_data = g_hash_table_lookup(self->by_obj, &track_data->obj); + nm_assert(obj_data); + nm_assert(c_list_contains(&obj_data->obj_lst_head, &track_data->obj_lst)); + nm_assert(obj_data == g_hash_table_lookup(self->by_obj, &track_data->obj)); + + if (make_owned_by_us) { + if (obj_data->config_state == CONFIG_STATE_NONE) { + /* we need to mark this entry that it requires a touch on the next + * sync. */ + obj_data->config_state = CONFIG_STATE_OWNED_BY_US; + } + } else if (remove_user_tag_data && c_list_length_is(&track_data->user_tag_lst, 1)) + g_hash_table_remove(self->by_user_tag, &track_data->user_tag); + + /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry + * around for the next sync -- so that we can undo what we did earlier. */ + if (obj_data->config_state == CONFIG_STATE_NONE && c_list_length_is(&track_data->obj_lst, 1)) + g_hash_table_remove(self->by_obj, &track_data->obj); + + g_hash_table_remove(self->by_data, track_data); +} + +gboolean +nmp_global_tracker_untrack(NMPGlobalTracker *self, + NMPObjectType obj_type, + gconstpointer obj, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + TrackData *track_data; + gboolean changed = FALSE; + + g_return_val_if_fail(NMP_IS_GLOBAL_TRACKER(self), FALSE); + nm_assert(NM_IN_SET(obj_type, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE, + NMP_OBJECT_TYPE_MPTCP_ADDR)); + g_return_val_if_fail(obj, FALSE); + g_return_val_if_fail(user_tag, FALSE); + + p_obj_stack = _obj_stackinit(&obj_stack, obj_type, obj); + + track_data = _track_data_lookup(self->by_data, p_obj_stack, user_tag); + if (track_data) { + _track_data_untrack(self, track_data, TRUE, FALSE); + changed = TRUE; + } + + return changed; +} + +void +nmp_global_tracker_set_dirty(NMPGlobalTracker *self, gconstpointer user_tag) +{ + TrackData *track_data; + TrackUserTagData *user_tag_data; + + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + g_return_if_fail(user_tag); + + user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry (track_data, &user_tag_data->user_tag_lst_head, user_tag_lst) + track_data->dirty = TRUE; +} + +gboolean +nmp_global_tracker_untrack_all(NMPGlobalTracker *self, + gconstpointer user_tag, + gboolean all /* or only dirty */, + gboolean make_survivors_dirty) +{ + TrackData *track_data; + TrackData *track_data_safe; + TrackUserTagData *user_tag_data; + gboolean changed = FALSE; + + g_return_val_if_fail(NMP_IS_GLOBAL_TRACKER(self), FALSE); + g_return_val_if_fail(user_tag, FALSE); + + user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); + if (!user_tag_data) + return FALSE; + + c_list_for_each_entry_safe (track_data, + track_data_safe, + &user_tag_data->user_tag_lst_head, + user_tag_lst) { + if (all || track_data->dirty) { + _track_data_untrack(self, track_data, FALSE, FALSE); + changed = TRUE; + continue; + } + if (make_survivors_dirty) + track_data->dirty = TRUE; + } + if (c_list_is_empty(&user_tag_data->user_tag_lst_head)) + g_hash_table_remove(self->by_user_tag, user_tag_data); + + return changed; +} + +/*****************************************************************************/ + +/* Usually, we track NMPlatformMptcpAddr instances with an ifindex set. + * If we have *any* such instance, we know that the ifindex is fully + * synched (meaning, we will delete all unknown endpoints for that interface). + * However, if we don't have an endpoint on the interface, we may still + * want to track that a certain ifindex is fully managed. + * + * This initializes a dummy instance for exactly that purpose. */ +const NMPlatformMptcpAddr * +nmp_global_tracker_mptcp_addr_init_for_ifindex(NMPlatformMptcpAddr *addr, int ifindex) +{ + nm_assert(addr); + nm_assert(ifindex > 0); + + *addr = (NMPlatformMptcpAddr){ + .ifindex = ifindex, + .addr_family = AF_UNSPEC, + }; + + return addr; +} + +/*****************************************************************************/ + +typedef struct { + TrackObjData *obj_data; + const TrackData *td_best; +} MptcpSyncData; + +static int +_mptcp_entries_cmp(gconstpointer a, gconstpointer b, gpointer user_data) +{ + const MptcpSyncData *d_a = a; + const MptcpSyncData *d_b = b; + + /* 1) prefer addresses based on the priority (highest priority + * sorted first). */ + NM_CMP_FIELD(d_b->td_best, d_a->td_best, track_priority_val); + + /* Finally, we only care about the order in which they were tracked. + * Rely on the stable sort to get that right. */ + return 0; +} + +void +nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply) +{ + char sbuf[64 + NM_UTILS_TO_STRING_BUFFER_SIZE]; + gs_unref_ptrarray GPtrArray *kaddrs_arr = NULL; + gs_unref_hashtable GHashTable *kaddrs_idx = NULL; + TrackObjData *obj_data; + TrackObjData *obj_data_safe; + CList *by_obj_lst_head; + const TrackData *td_best; + gs_unref_hashtable GHashTable *handled_ifindexes = NULL; + gs_unref_array GArray *entries = NULL; + gs_unref_hashtable GHashTable *entries_hash_by_addr = NULL; + gs_unref_hashtable GHashTable *entries_to_delete = NULL; + guint i; + guint j; + + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + + _LOGD("sync mptcp-addr%s", reapply ? " (reapply)" : ""); + + /* Iterate over the tracked objects and construct @handled_ifindexes, @entries + * and @entries_to_delete. + * - @handled_ifindexes is a hash with all managed interfaces (their ifindex). + * - @entries are the MptcpSyncData instances for the tracked objects. + * - @entries_to_delete are the NMPObject which we added earlier, but now not + * anymore (and which we shall delete). */ + by_obj_lst_head = _by_obj_lst_head(self, NMP_OBJECT_TYPE_MPTCP_ADDR); + c_list_for_each_entry_safe (obj_data, obj_data_safe, by_obj_lst_head, by_obj_lst) { + const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(obj_data->obj); + NMPlatformMptcpAddr xtst; + + nm_assert(mptcp_addr->port == 0); + nm_assert(mptcp_addr->ifindex > 0); + nm_assert(mptcp_addr->id == 0); + nm_assert_addr_family_or_unspec(mptcp_addr->addr_family); + + /* AF_UNSPEC means this is the dummy object. We only care about it to make the + * ifindex as managed via @handled_ifindexes. */ + nm_assert( + (mptcp_addr->addr_family == AF_UNSPEC) + == (nm_platform_mptcp_addr_cmp( + mptcp_addr, + nmp_global_tracker_mptcp_addr_init_for_ifindex(&xtst, mptcp_addr->ifindex)) + == 0)); + + /* We need to know which ifindexes are managed/handled by us. Build an index + * for that. */ + if (!handled_ifindexes) + handled_ifindexes = g_hash_table_new(nm_direct_hash, NULL); + g_hash_table_add(handled_ifindexes, GINT_TO_POINTER(mptcp_addr->ifindex)); + + td_best = _track_obj_data_get_best_data(obj_data); + + if (!td_best) { + nm_assert(obj_data->config_state == CONFIG_STATE_ADDED_BY_US); + + /* This entry is a tombstone, that tells us that added the object earlier. + * We can delete the MPTCP address (if it's still configured). + * + * Then we can drop the tombstone. */ + + if (mptcp_addr->addr_family != AF_UNSPEC) { + if (!reapply) { + if (!entries_to_delete) { + entries_to_delete = g_hash_table_new_full((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + NULL); + } + g_hash_table_add(entries_to_delete, (gpointer) nmp_object_ref(obj_data->obj)); + } + } + + /* We can forget about this entry now. */ + g_hash_table_remove(self->by_obj, obj_data); + continue; + } + + /* negative and zero track priorities are not implemented (and make no sense?). */ + nm_assert(td_best->track_priority_val > 0); + nm_assert(td_best->track_priority_present); + + if (mptcp_addr->addr_family == AF_UNSPEC) { + /* This is a nmp_global_tracker_mptcp_addr_init_for_ifindex() dummy entry. + * It only exists so we can add the @handled_ifindexes entry above + * and handle addresses on this interface. */ + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + continue; + } + + if (!entries) + entries = g_array_new(FALSE, FALSE, sizeof(MptcpSyncData)); + + g_array_append_val(entries, + ((const MptcpSyncData){ + .obj_data = obj_data, + .td_best = td_best, + })); + } + /* We collected all the entires we want to configure. Now, sort them by + * priority, and drop all the duplicates (preferring the entries that + * appear first, where first means "older"). In kernel, we can only configure an IP + * address (without port) as endpoint once. If two interfaces provide the same IP + * address, we can only configure one. We need to select one and filter out duplicates. + * While there is no solution, the idea is to select the preferred address + * somewhat consistently. + * + * Also, create a lookup index @entries_hash_by_addr to lookup by address. */ + if (entries) { + /* First we sort the entries by priority, to prefer the ones with higher + * priority. In case of equal priority, we rely on the stable sort to + * preserve the order in which things got tracked. */ + g_array_sort_with_data(entries, _mptcp_entries_cmp, NULL); + + entries_hash_by_addr = g_hash_table_new(nm_platform_mptcp_addr_index_addr_cmp, + nm_platform_mptcp_addr_index_addr_equal); + + /* Now, drop all duplicates addresses. Only keep the first one. */ + for (i = 0, j = 0; i < entries->len; i++) { + const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); + + obj_data = g_hash_table_lookup(entries_hash_by_addr, (gpointer) mptcp_addr); + if (obj_data) { + /* This object is shadowed. We ignore it. + * + * However, we first propagate the config_state. For MPTCP addrs, it can only be + * NONE or ADDED_BY_US. */ + nm_assert(NM_IN_SET(d->obj_data->config_state, + CONFIG_STATE_NONE, + CONFIG_STATE_ADDED_BY_US)); + nm_assert( + NM_IN_SET(obj_data->config_state, CONFIG_STATE_NONE, CONFIG_STATE_ADDED_BY_US)); + + if (d->obj_data->config_state == CONFIG_STATE_ADDED_BY_US) { + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + d->obj_data->config_state = CONFIG_STATE_NONE; + } + continue; + } + + if (!g_hash_table_insert(entries_hash_by_addr, (gpointer) mptcp_addr, d->obj_data)) + nm_assert_not_reached(); + + if (i != j) + *(nm_g_array_index_p(entries, MptcpSyncData, j)) = *d; + j++; + + if (j >= MPTCP_PM_ADDR_MAX) { + /* Kernel limits the number of addresses we can configure. + * It's hard-coded here, taken from current kernel. Hopefully + * it matches the running kernel. + * + * It's worse. There might be other addresses already configured + * on other interfaces (or with a port). Our sync method will leave + * them alone, as they were not added by us. So the actual limit + * is possibly smaller, and kernel fails with EINVAL. + * + * Still, we definitely need to truncate the list here. Imagine + * during an earlier sync we added MAX addresses on one interface. + * Now, another interface activates, and wants to configure one + * address. That address will get a higher priority (chosen by NML3Cfg), + * so that part is good. However, it means we must drop the last from + * the other MAX addresses. We achieve that by truncating the list + * to MPTCP_PM_ADDR_MAX. + */ + break; + } + } + g_array_set_size(entries, j); + } + + /* Get the list of currently (in kernel) configured MPTCP endpoints. */ + kaddrs_arr = nm_platform_mptcp_addrs_dump(self->platform); + + /* First, delete all kaddrs which we no longer want... */ + if (kaddrs_arr) { + for (i = 0; i < kaddrs_arr->len; i++) { + const NMPObject *obj = kaddrs_arr->pdata[i]; + const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(obj); + gboolean add_to_kaddr_idx = FALSE; + + if (mptcp_addr->port != 0 || mptcp_addr->ifindex <= 0) { + /* We ignore all endpoints that have a port or no ifindex. + * Those were never created by us, let the user who created + * them handle them. */ + goto keep_and_next; + } + + if (!nm_g_hash_table_contains(handled_ifindexes, + GINT_TO_POINTER(mptcp_addr->ifindex))) { + /* This endpoint is on an interface we don't manage. Ignore (and keep) it. */ + goto keep_and_next; + } + + /* We have the object in the delete-list. However, we might still also want + * to add it back. Check for that too. */ + obj_data = nm_g_hash_table_lookup(entries_hash_by_addr, mptcp_addr); + if (obj_data) { + const NMPlatformMptcpAddr *mptcp_addr2 = NMP_OBJECT_CAST_MPTCP_ADDR(obj_data->obj); + + if (mptcp_addr->flags == mptcp_addr2->flags + && mptcp_addr->ifindex == mptcp_addr2->ifindex) { + /* We want to add this address and it's already configured. Keep it + * and remember that we already have it. */ + add_to_kaddr_idx = TRUE; + goto keep_and_next; + } + + /* We want to configure a similar address mptcp_addr2) as the one that is already configured + * (mptcp_addr). However, the ifindex or flag differs. Delete this one to add the + * right one blow. */ + } else { + /* We don't want to configure this address (anymore). */ + if (reapply) { + /* in reapply mode, we delete the extra address. */ + } else { + /* Otherwise, we only delete it, if we remember that we added this one + * before. */ + if (!nm_g_hash_table_contains(entries_to_delete, obj)) { + /* This address was not added by us. Keep it. */ + goto keep_and_next; + } + } + } + + if (!nm_platform_object_delete(self->platform, obj)) { + /* We failed to delete it. It's unclear what is the matter with this + * object. Ignore the failure. */ + } + + continue; + +keep_and_next: + _LOGt("keep: %s \"%s\"%s", + NMP_OBJECT_GET_CLASS(obj)->obj_type_name, + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)), + add_to_kaddr_idx ? " (index)" : ""); + if (add_to_kaddr_idx) { + if (!kaddrs_idx) { + kaddrs_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + } + g_hash_table_add(kaddrs_idx, (gpointer) obj); + } + } + } + + if (entries) { + for (i = 0; i < entries->len; i++) { + const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i); + const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj); + const NMPObject *kobj; + + nm_assert(mptcp_addr->port == 0); + nm_assert(mptcp_addr->ifindex > 0); + + d->obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + + kobj = nm_g_hash_table_lookup(kaddrs_idx, d->obj_data->obj); + if (kobj && kobj->mptcp_addr.flags == mptcp_addr->flags) { + /* This address is already added with the right flags. We can + * skip it. */ + continue; + } + + /* Kernel actually only allows us to add a small number of addresses. + * Also, if we have a conflicting address on another interface, the + * request will be rejected. + * + * Don't try to handle that. Just attempt to add the address, and if + * we fail, there is nothing we can do about it. */ + nm_platform_mptcp_addr_update(self->platform, TRUE, mptcp_addr); + } + } +} + +void +nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean keep_deleted) +{ + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + const NMDedupMultiHeadEntry *pl_head_entry; + const NMPObject *plobj; + gs_unref_ptrarray GPtrArray *objs_to_delete = NULL; + TrackObjData *obj_data; + TrackObjData *obj_data_safe; + CList *by_obj_lst_head; + guint i; + const TrackData *td_best; + + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + g_return_if_fail(NM_IN_SET(obj_type, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE, + NMP_OBJECT_TYPE_ROUTING_RULE)); + + _LOGD("sync %s%s", + nmp_class_from_type(obj_type)->obj_type_name, + keep_deleted ? " (don't remove any)" : ""); + + if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) + pl_head_entry = nm_platform_lookup_obj_type(self->platform, obj_type); + else + pl_head_entry = nm_platform_lookup_object(self->platform, obj_type, 0); + + if (pl_head_entry) { + NMDedupMultiIter pl_iter; + + nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { + obj_data = g_hash_table_lookup(self->by_obj, &plobj); + + if (!obj_data) { + /* this obj is not tracked. It was externally added, hence we + * ignore it. */ + continue; + } + + td_best = _track_obj_data_get_best_data(obj_data); + if (td_best) { + if (td_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + continue; + } + if (td_best->track_priority_val == 0) { + if (!NM_IN_SET(obj_data->config_state, + CONFIG_STATE_ADDED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; + continue; + } + obj_data->config_state = CONFIG_STATE_NONE; + } + } + + if (keep_deleted) { + _LOGD("forget/leak object added by us: %s \"%s\"", + NMP_OBJECT_GET_CLASS(plobj)->obj_type_name, + nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + continue; + } + + if (!objs_to_delete) + objs_to_delete = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add(objs_to_delete, (gpointer) nmp_object_ref(plobj)); + + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; + } + } + + if (objs_to_delete) { + for (i = 0; i < objs_to_delete->len; i++) + nm_platform_object_delete(self->platform, objs_to_delete->pdata[i]); + } + + by_obj_lst_head = _by_obj_lst_head(self, obj_type); + + c_list_for_each_entry_safe (obj_data, obj_data_safe, by_obj_lst_head, by_obj_lst) { + nm_assert(NMP_OBJECT_GET_TYPE(obj_data->obj) == obj_type); + + td_best = _track_obj_data_get_best_data(obj_data); + + if (!td_best) { + g_hash_table_remove(self->by_obj, obj_data); + continue; + } + + if (!td_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; + continue; + } + if (td_best->track_priority_val == 0) { + if (!NM_IN_SET(obj_data->config_state, + CONFIG_STATE_REMOVED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; + continue; + } + obj_data->config_state = CONFIG_STATE_NONE; + } + + plobj = + nm_platform_lookup_obj(self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); + if (plobj) { + int c; + + switch (obj_type) { + case NMP_OBJECT_TYPE_ROUTING_RULE: + c = nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj), + NMP_OBJECT_CAST_ROUTING_RULE(plobj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY); + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + c = nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(obj_data->obj), + NMP_OBJECT_CAST_IP4_ROUTE(plobj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY); + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + c = nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(obj_data->obj), + NMP_OBJECT_CAST_IP6_ROUTE(plobj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY); + break; + default: + c = nm_assert_unreachable_val(0); + break; + } + if (c == 0) + continue; + nm_platform_object_delete(self->platform, plobj); + } + + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + + if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) { + nm_platform_routing_rule_add(self->platform, + NMP_NLM_FLAG_ADD, + NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj)); + } else + nm_platform_ip_route_add(self->platform, NMP_NLM_FLAG_APPEND, obj_data->obj); + } +} + +/*****************************************************************************/ + +void +nmp_global_tracker_track_rule_from_platform(NMPGlobalTracker *self, + NMPlatform *platform, + int addr_family, + gint32 tracking_priority, + gconstpointer user_tag) +{ + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + + if (!platform) + platform = self->platform; + else + g_return_if_fail(NM_IS_PLATFORM(platform)); + + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup(platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE(o); + + if (addr_family != AF_UNSPEC && rr->addr_family != addr_family) + continue; + + nmp_global_tracker_track_rule(self, rr, tracking_priority, user_tag, NULL); + } +} + +/*****************************************************************************/ + +void +nmp_global_tracker_track_rule_default(NMPGlobalTracker *self, + int addr_family, + gint32 track_priority, + gconstpointer user_tag) +{ + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + + nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + /* track the default rules. See also `man ip-rule`. */ + + if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) { + nmp_global_tracker_track_rule(self, + &((NMPlatformRoutingRule){ + .addr_family = AF_INET, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag, + NULL); + nmp_global_tracker_track_rule(self, + &((NMPlatformRoutingRule){ + .addr_family = AF_INET, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag, + NULL); + nmp_global_tracker_track_rule(self, + &((NMPlatformRoutingRule){ + .addr_family = AF_INET, + .priority = 32767, + .table = RT_TABLE_DEFAULT, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag, + NULL); + } + if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { + nmp_global_tracker_track_rule(self, + &((NMPlatformRoutingRule){ + .addr_family = AF_INET6, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag, + NULL); + nmp_global_tracker_track_rule(self, + &((NMPlatformRoutingRule){ + .addr_family = AF_INET6, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag, + NULL); + } +} + +/*****************************************************************************/ + +NMPGlobalTracker * +nmp_global_tracker_new(NMPlatform *platform) +{ + NMPGlobalTracker *self; + + g_return_val_if_fail(NM_IS_PLATFORM(platform), NULL); + + G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(TrackUserTagData, user_tag) == 0); + + self = g_slice_new(NMPGlobalTracker); + *self = (NMPGlobalTracker){ + .ref_count = 1, + .platform = g_object_ref(platform), + .by_data = + g_hash_table_new_full(_track_data_hash, _track_data_equal, NULL, _track_data_destroy), + .by_obj = g_hash_table_new_full(_track_obj_data_hash, + _track_obj_data_equal, + NULL, + _track_obj_data_destroy), + .by_user_tag = g_hash_table_new_full(nm_pdirect_hash, + nm_pdirect_equal, + NULL, + _track_user_tag_data_destroy), + .by_obj_lst_heads[0] = C_LIST_INIT(self->by_obj_lst_heads[0]), + .by_obj_lst_heads[1] = C_LIST_INIT(self->by_obj_lst_heads[1]), + .by_obj_lst_heads[2] = C_LIST_INIT(self->by_obj_lst_heads[2]), + .by_obj_lst_heads[3] = C_LIST_INIT(self->by_obj_lst_heads[3]), + }; + return self; +} + +NMPGlobalTracker * +nmp_global_tracker_ref(NMPGlobalTracker *self) +{ + g_return_val_if_fail(NMP_IS_GLOBAL_TRACKER(self), NULL); + + self->ref_count++; + return self; +} + +void +nmp_global_tracker_unref(NMPGlobalTracker *self) +{ + g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self)); + + if (--self->ref_count > 0) + return; + + g_hash_table_destroy(self->by_user_tag); + g_hash_table_destroy(self->by_obj); + g_hash_table_destroy(self->by_data); + nm_assert(c_list_is_empty(&self->by_obj_lst_heads[0])); + nm_assert(c_list_is_empty(&self->by_obj_lst_heads[1])); + nm_assert(c_list_is_empty(&self->by_obj_lst_heads[2])); + nm_assert(c_list_is_empty(&self->by_obj_lst_heads[3])); + g_object_unref(self->platform); + nm_g_slice_free(self); +} diff --git a/src/libnm-platform/nmp-global-tracker.h b/src/libnm-platform/nmp-global-tracker.h new file mode 100644 index 00000000..08a4d85f --- /dev/null +++ b/src/libnm-platform/nmp-global-tracker.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef __NMP_GLOBAL_TRACKER_H__ +#define __NMP_GLOBAL_TRACKER_H__ + +#include "nm-platform.h" + +/*****************************************************************************/ + +#define NMP_GLOBAL_TRACKER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_global_tracker_new) + +typedef struct _NMPGlobalTracker NMPGlobalTracker; + +NMPGlobalTracker *nmp_global_tracker_new(NMPlatform *platform); + +NMPGlobalTracker *nmp_global_tracker_ref(NMPGlobalTracker *self); +void nmp_global_tracker_unref(NMPGlobalTracker *self); + +#define nm_auto_unref_global_tracker nm_auto(_nmp_global_tracker_unref) +NM_AUTO_DEFINE_FCN0(NMPGlobalTracker *, _nmp_global_tracker_unref, nmp_global_tracker_unref); + +gboolean nmp_global_tracker_track(NMPGlobalTracker *self, + NMPObjectType obj_type, + gconstpointer obj, + gint32 track_priority, + gconstpointer user_tag, + gconstpointer user_tag_untrack); + +static inline gboolean +nmp_global_tracker_track_rule(NMPGlobalTracker *self, + const NMPlatformRoutingRule *routing_rule, + gint32 track_priority, + gconstpointer user_tag, + gconstpointer user_tag_untrack) +{ + return nmp_global_tracker_track(self, + NMP_OBJECT_TYPE_ROUTING_RULE, + routing_rule, + track_priority, + user_tag, + user_tag_untrack); +} + +void nmp_global_tracker_track_rule_default(NMPGlobalTracker *self, + int addr_family, + gint32 track_priority, + gconstpointer user_tag); + +void nmp_global_tracker_track_rule_from_platform(NMPGlobalTracker *self, + NMPlatform *platform, + int addr_family, + gint32 tracking_priority, + gconstpointer user_tag); + +gboolean nmp_global_tracker_untrack(NMPGlobalTracker *self, + NMPObjectType obj_type, + gconstpointer obj, + gconstpointer user_tag); + +static inline gboolean +nmp_global_tracker_untrack_rule(NMPGlobalTracker *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag) +{ + return nmp_global_tracker_untrack(self, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule, user_tag); +} + +void nmp_global_tracker_set_dirty(NMPGlobalTracker *self, gconstpointer user_tag); + +gboolean nmp_global_tracker_untrack_all(NMPGlobalTracker *self, + gconstpointer user_tag, + gboolean all /* or only dirty */, + gboolean make_survivors_dirty); + +void nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean keep_deleted); + +void nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply); + +/*****************************************************************************/ + +const NMPlatformMptcpAddr *nmp_global_tracker_mptcp_addr_init_for_ifindex(NMPlatformMptcpAddr *addr, + int ifindex); + +#endif /* __NMP_GLOBAL_TRACKER_H__ */ diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index 6c177b4c..d06aa9cd 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -20,25 +20,26 @@ /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG(level, obj, ...) \ - G_STMT_START \ - { \ - const NMLogLevel __level = (level); \ - \ - if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ - const NMPObject *const __obj = (obj); \ - \ - _nm_log(__level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __obj, \ - (__obj ? NMP_OBJECT_GET_CLASS(__obj)->obj_type_name \ - : "???") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, obj, ...) \ + G_STMT_START \ + { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled(__level, _NMLOG_DOMAIN)) { \ + const NMPObject *const __obj = (obj); \ + \ + _nm_log(__level, \ + _NMLOG_DOMAIN, \ + 0, \ + NULL, \ + NULL, \ + "nmp-object[" NM_HASH_OBFUSCATE_PTR_FMT "" \ + "/%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(__obj), \ + (__obj ? NMP_OBJECT_GET_CLASS(__obj)->obj_type_name \ + : "???") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END /*****************************************************************************/ @@ -380,7 +381,8 @@ _idx_obj_part(const DedupMultiIdxType *idx_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER) + NMP_OBJECT_TYPE_TFILTER, + NMP_OBJECT_TYPE_MPTCP_ADDR) || !nmp_object_is_visible(obj_a)) { if (h) nm_hash_update_val(h, obj_a); @@ -805,13 +807,9 @@ _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) nm_assert(obj); nm_assert(klass); - *obj = (NMPObject){ - .parent = - { - .klass = (const NMDedupMultiObjClass *) klass, - ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, - }, - }; + memset(obj, 0, sizeof(NMPObject)); + obj->_class = klass; + obj->parent._ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; } @@ -844,6 +842,17 @@ nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src) _nmp_object_stackinit_from_class(obj, klass); if (klass->cmd_plobj_id_copy) klass->cmd_plobj_id_copy(&obj->object, &src->object); + else { + /* This object must not implement cmd_obj_copy(). + * If it would, it would mean that we require a deep copy + * of the data. As @obj is stack-allocated, it cannot track + * ownership. The caller must not use nmp_object_stackinit_id() + * with an object of such a type. */ + nm_assert(!klass->cmd_obj_copy); + + /* plain memcpy of the public part suffices. */ + memcpy(&obj->object, &src->object, klass->sizeof_data); + } return obj; } @@ -904,7 +913,7 @@ nmp_object_to_string(const NMPObject *obj, switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: if (!klass->cmd_plobj_to_string_id) { - g_snprintf(buf, buf_size, "%p", obj); + g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); return buf; } return klass->cmd_plobj_to_string_id(&obj->object, buf, buf_size); @@ -912,9 +921,9 @@ nmp_object_to_string(const NMPObject *obj, g_snprintf( buf, buf_size, - "[%s,%p,%u,%calive,%cvisible; %s]", + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", klass->obj_type_name, - obj, + NM_HASH_OBFUSCATE_PTR(obj), obj->parent._ref_count, nmp_object_is_alive(obj) ? '+' : '-', nmp_object_is_visible(obj) ? '+' : '-', @@ -943,14 +952,15 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, case NMP_OBJECT_TO_STRING_ALL: nm_strbuf_append(&b, &buf_size, - "[%s,%p,%u,%calive,%cvisible,%cin-nl,%p; ", + "[%s," NM_HASH_OBFUSCATE_PTR_FMT + ",%u,%calive,%cvisible,%cin-nl," NM_HASH_OBFUSCATE_PTR_FMT "; ", klass->obj_type_name, - obj, + NM_HASH_OBFUSCATE_PTR(obj), obj->parent._ref_count, nmp_object_is_alive(obj) ? '+' : '-', nmp_object_is_visible(obj) ? '+' : '-', obj->_link.netlink.is_in_netlink ? '+' : '-', - obj->_link.udev.device); + NM_HASH_OBFUSCATE_PTR(obj->_link.udev.device)); NMP_OBJECT_GET_CLASS(obj)->cmd_plobj_to_string(&obj->object, b, buf_size); nm_strbuf_seek_end(&b, &buf_size); if (obj->_link.netlink.lnk) { @@ -988,15 +998,15 @@ _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - g_snprintf(buf, buf_size, "%p", obj); + g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); return buf; case NMP_OBJECT_TO_STRING_ALL: g_snprintf(buf, buf_size, - "[%s,%p,%u,%calive,%cvisible; %s]", + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", klass->obj_type_name, - obj, + NM_HASH_OBFUSCATE_PTR(obj), obj->parent._ref_count, nmp_object_is_alive(obj) ? '+' : '-', nmp_object_is_visible(obj) ? '+' : '-', @@ -1052,17 +1062,17 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: - g_snprintf(buf, buf_size, "%p", obj); + g_snprintf(buf, buf_size, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR(obj)); return buf; case NMP_OBJECT_TO_STRING_ALL: b = buf; nm_strbuf_append(&b, &buf_size, - "[%s,%p,%u,%calive,%cvisible; %s" + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s" "%s", klass->obj_type_name, - obj, + NM_HASH_OBFUSCATE_PTR(obj), obj->parent._ref_count, nmp_object_is_alive(obj) ? '+' : '-', nmp_object_is_visible(obj) ? '+' : '-', @@ -1153,10 +1163,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); } @@ -1236,7 +1243,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) @@ -1428,21 +1434,6 @@ _vt_cmd_plobj_id_copy(ip6_address, NMPlatformIP6Address, { dst->address = src->address; }); -_vt_cmd_plobj_id_copy(ip4_route, NMPlatformIP4Route, { - *dst = *src; - nm_assert(nm_platform_ip4_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); -}); - -_vt_cmd_plobj_id_copy(ip6_route, NMPlatformIP6Route, { - *dst = *src; - nm_assert(nm_platform_ip6_route_cmp(dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); -}); - -_vt_cmd_plobj_id_copy(routing_rule, NMPlatformRoutingRule, { - *dst = *src; - nm_assert(nm_platform_routing_rule_cmp(dst, src, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); -}); - /* Uses internally nmp_object_copy(), hence it also violates the const * promise for @obj. * */ @@ -1561,6 +1552,37 @@ _vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatform NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); } +_vt_cmd_plobj_id_cmp(mptcp_addr, NMPlatformMptcpAddr, { + /* The primary key of an MPTCP endpoint is only the address:port@ifindex. + * + * Which does not fully correspond to kernel's view. Kernel's view is determined + * by the question whether you can add two objects that only differ by one + * attribute. If you can, the attribute is part of the ID otherwise it isn't. + * + * Note that for kernel, the "ifindex" is not part of the identity. + * That is, you cannot two add two endpoints that only differ by + * ifindex. However, for our purpose, it is very useful to make + * the "ifindex" part of the identity. For example, NMPGlobalTracker will use + * this to track MPTCP addresses from independent callers (NML3Cfg). + * It would be bad, if objects that differ by "ifindex" would be + * combined. + * + * The "id" is intentionally not part of the identity for us. Note however, that kernel + * does not allow configuring duplicates "id" -- so the "id" could be a primary key + * as far as kernel is concerned. However, when we track MPTCP endpoints that + * we want to configure, the "id" is left undefined (and we let kernel choose it). + * If the "id" would be part of the NMPObject's ID, we could not lookup + * an object unless we know the "id" -- which we often don't. + */ + NM_CMP_FIELD(obj1, obj2, ifindex); + NM_CMP_FIELD(obj1, obj2, addr_family); + NM_CMP_FIELD_MEMCMP_LEN(obj1, + obj2, + addr, + nm_utils_addr_family_to_size_untrusted(obj1->addr_family)); + NM_CMP_FIELD(obj1, obj2, port); +}); + void nmp_object_id_hash_update(const NMPObject *obj, NMHashState *h) { @@ -1646,6 +1668,12 @@ _vt_cmd_plobj_id_hash_update(tfilter, NMPlatformTfilter, { nm_hash_update_vals(h, obj->ifindex, obj->handle); }); +_vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, { + /* See the corresponding ID cmp function for details. */ + nm_hash_update_vals(h, obj->addr_family, obj->port, obj->ifindex); + nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size_untrusted(obj->addr_family)); +}); + static void _vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h) { @@ -1754,6 +1782,12 @@ _vt_cmd_obj_is_alive_tfilter(const NMPObject *obj) return NMP_OBJECT_CAST_TFILTER(obj)->ifindex > 0; } +static gboolean +_vt_cmd_obj_is_alive_mptcp_addr(const NMPObject *obj) +{ + return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6, AF_UNSPEC); +} + gboolean nmp_object_is_visible(const NMPObject *obj) { @@ -2099,6 +2133,7 @@ nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type) case NMP_OBJECT_TYPE_ROUTING_RULE: case NMP_OBJECT_TYPE_QDISC: case NMP_OBJECT_TYPE_TFILTER: + case NMP_OBJECT_TYPE_MPTCP_ADDR: _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE; return _L(lookup); @@ -2122,7 +2157,7 @@ nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname) } const NMPLookup * -nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex) +nmp_lookup_init_object_by_ifindex(NMPLookup *lookup, NMPObjectType obj_type, int ifindex) { NMPObject *o; @@ -2133,19 +2168,11 @@ nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex) NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, NMP_OBJECT_TYPE_QDISC, - NMP_OBJECT_TYPE_TFILTER)); - - if (G_UNLIKELY( - (ifindex < 0) - || (ifindex == 0 - && !NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)))) { - /* This function used to have a fallback that meant to lookup all objects, if - * ifindex is non-positive. As routes can have a zero ifindex, that fallback is - * confusing and no longer supported. Only have this code, to catch accidental bugs - * after the API change. */ - nm_assert_not_reached(); - return nmp_lookup_init_obj_type(lookup, obj_type); - } + NMP_OBJECT_TYPE_TFILTER, + NMP_OBJECT_TYPE_MPTCP_ADDR)); + nm_assert(ifindex > 0 + || (ifindex == 0 + && NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE))); o = _nmp_object_stackinit_from_type(&lookup->selector_obj, obj_type); o->obj_with_ifindex.ifindex = ifindex; @@ -3183,7 +3210,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string, @@ -3204,7 +3230,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, .supported_cache_ids = _supported_cache_ids_ipx_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route, .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_route, .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string, @@ -3224,7 +3249,6 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .signal_type = NM_PLATFORM_SIGNAL_ROUTING_RULE_CHANGED, .supported_cache_ids = _supported_cache_ids_routing_rules, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_routing_rule, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_routing_rule, .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_routing_rule, .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_routing_rule, .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string, @@ -3485,4 +3509,32 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_wireguard_hash_update, .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_wireguard_cmp, }, + [NMP_OBJECT_TYPE_LNK_BOND - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_LNK_BOND, + .sizeof_data = sizeof(NMPObjectLnkBond), + .sizeof_public = sizeof(NMPlatformLnkBond), + .obj_type_name = "bond", + .lnk_link_type = NM_LINK_TYPE_BOND, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_lnk_bond_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_lnk_bond_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_lnk_bond_cmp, + }, + [NMP_OBJECT_TYPE_MPTCP_ADDR - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_MPTCP_ADDR, + .sizeof_data = sizeof(NMPObjectMptcpAddr), + .sizeof_public = sizeof(NMPlatformMptcpAddr), + .obj_type_name = "mptcp-addr", + .supported_cache_ids = _supported_cache_ids_object, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_mptcp_addr, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_mptcp_addr, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_mptcp_addr, + .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_mptcp_addr_to_string, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_mptcp_addr_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_mptcp_addr_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_mptcp_addr_cmp, + }, }; diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index d6b88ba2..4958404a 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 { @@ -250,6 +245,10 @@ typedef struct { } NMPObjectLnkBridge; typedef struct { + NMPlatformLnkBond _public; +} NMPObjectLnkBond; + +typedef struct { NMPlatformLnkGre _public; } NMPObjectLnkGre; @@ -336,6 +335,10 @@ typedef struct { NMPlatformTfilter _public; } NMPObjectTfilter; +typedef struct { + NMPlatformMptcpAddr _public; +} NMPObjectMptcpAddr; + struct _NMPObject { union { NMDedupMultiObj parent; @@ -352,6 +355,9 @@ struct _NMPObject { NMPlatformLnkBridge lnk_bridge; NMPObjectLnkBridge _lnk_bridge; + NMPlatformLnkBond lnk_bond; + NMPObjectLnkBond _lnk_bond; + NMPlatformLnkGre lnk_gre; NMPObjectLnkGre _lnk_gre; @@ -409,6 +415,9 @@ struct _NMPObject { NMPObjectQdisc _qdisc; NMPlatformTfilter tfilter; NMPObjectTfilter _tfilter; + + NMPlatformMptcpAddr mptcp_addr; + NMPObjectMptcpAddr _mptcp_addr; }; }; @@ -493,6 +502,7 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) case NMP_OBJECT_TYPE_TFILTER: case NMP_OBJECT_TYPE_LNK_BRIDGE: + case NMP_OBJECT_TYPE_LNK_BOND: case NMP_OBJECT_TYPE_LNK_GRE: case NMP_OBJECT_TYPE_LNK_GRETAP: case NMP_OBJECT_TYPE_LNK_INFINIBAND: @@ -509,6 +519,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) case NMP_OBJECT_TYPE_LNK_VRF: case NMP_OBJECT_TYPE_LNK_VXLAN: case NMP_OBJECT_TYPE_LNK_WIREGUARD: + + case NMP_OBJECT_TYPE_MPTCP_ADDR: return TRUE; case NMP_OBJECT_TYPE_ROUTING_RULE: @@ -570,6 +582,8 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX(NMPObjectType obj_type) _NMP_OBJECT_CAST(obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) #define NMP_OBJECT_CAST_LNK_BRIDGE(obj) \ _NMP_OBJECT_CAST(obj, lnk_bridge, NMP_OBJECT_TYPE_LNK_BRIDGE) +#define NMP_OBJECT_CAST_MPTCP_ADDR(obj) \ + _NMP_OBJECT_CAST(obj, mptcp_addr, NMP_OBJECT_TYPE_MPTCP_ADDR) static inline int NMP_OBJECT_TYPE_TO_ADDR_FAMILY(NMPObjectType obj_type) @@ -792,7 +806,8 @@ nmp_cache_lookup(const NMPCache *cache, const NMPLookup *lookup) const NMPLookup *nmp_lookup_init_obj_type(NMPLookup *lookup, NMPObjectType obj_type); const NMPLookup *nmp_lookup_init_link_by_ifname(NMPLookup *lookup, const char *ifname); -const NMPLookup *nmp_lookup_init_object(NMPLookup *lookup, NMPObjectType obj_type, int ifindex); +const NMPLookup * +nmp_lookup_init_object_by_ifindex(NMPLookup *lookup, NMPObjectType obj_type, int ifindex); const NMPLookup *nmp_lookup_init_route_default(NMPLookup *lookup, NMPObjectType obj_type); const NMPLookup *nmp_lookup_init_route_by_weak_id(NMPLookup *lookup, const NMPObject *obj); const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id(NMPLookup *lookup, @@ -1019,7 +1034,7 @@ nm_platform_lookup_object(NMPlatform *platform, NMPObjectType obj_type, int ifin { NMPLookup lookup; - nmp_lookup_init_object(&lookup, obj_type, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, obj_type, ifindex); return nm_platform_lookup(platform, &lookup); } @@ -1032,7 +1047,7 @@ nm_platform_lookup_object_clone(NMPlatform *platform, { NMPLookup lookup; - nmp_lookup_init_object(&lookup, obj_type, ifindex); + nmp_lookup_init_object_by_ifindex(&lookup, obj_type, ifindex); return nm_platform_lookup_clone(platform, &lookup, predicate, user_data); } diff --git a/src/libnm-platform/nmp-route-manager.c b/src/libnm-platform/nmp-route-manager.c deleted file mode 100644 index 842301b1..00000000 --- a/src/libnm-platform/nmp-route-manager.c +++ /dev/null @@ -1,844 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "libnm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nmp-route-manager.h" - -#include <linux/fib_rules.h> -#include <linux/rtnetlink.h> - -#include "libnm-log-core/nm-logging.h" -#include "libnm-std-aux/c-list-util.h" -#include "nmp-object.h" - -/*****************************************************************************/ - -struct _NMPRouteManager { - NMPlatform *platform; - GHashTable *by_obj; - GHashTable *by_user_tag; - GHashTable *by_data; - CList by_obj_lst_heads[3]; - guint ref_count; -}; - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "route-manager" - -#define _NMLOG(level, ...) __NMLOG_DEFAULT(level, LOGD_PLATFORM, _NMLOG_PREFIX_NAME, __VA_ARGS__) - -/*****************************************************************************/ - -static gboolean -NMP_IS_ROUTE_MANAGER(gpointer self) -{ - return self && ((NMPRouteManager *) self)->ref_count > 0 - && NM_IS_PLATFORM(((NMPRouteManager *) self)->platform); -} - -/*****************************************************************************/ - -typedef struct { - const NMPObject *obj; - gconstpointer user_tag; - CList obj_lst; - CList user_tag_lst; - - /* track_priority_val zero is special: those are weakly tracked rules. - * That means: NetworkManager will restore them only if it removed them earlier. - * But it will not remove or add them otherwise. - * - * Otherwise, the track_priority_val goes together with track_priority_present. - * In case of one rule being tracked multiple times (with different priorities), - * the one with higher priority wins. See _track_obj_data_get_best_data(). - * Then, the winning present state either enforces that the rule is present - * or absent. - * - * If a rules is not tracked at all, it is ignored by NetworkManager. Assuming - * that it was added externally by the user. But unlike weakly tracked rules, - * NM will *not* restore such rules if NetworkManager themself removed them. */ - guint32 track_priority_val; - bool track_priority_present : 1; - - bool dirty : 1; -} TrackData; - -typedef enum { - CONFIG_STATE_NONE = 0, - CONFIG_STATE_ADDED_BY_US = 1, - CONFIG_STATE_REMOVED_BY_US = 2, - - /* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE). - * - * Maybe we would only need to track whether we touched the rule at all. But we - * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US) - * or did we remove it (CONFIG_STATE_REMOVED_BY_US)? - * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively - * add/remove it, but whenever we are about to undo the add/remove, we need to do it. - * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally - * force the state next time when necessary. */ - CONFIG_STATE_OWNED_BY_US = 3, -} ConfigState; - -typedef struct { - const NMPObject *obj; - CList obj_lst_head; - - CList by_obj_lst; - - /* indicates whether we configured/removed the rule (during sync()). We need that, so - * if the rule gets untracked, that we know to remove/restore it. - * - * This makes NMPRouteManager stateful (beyond the configuration that indicates - * which rules are tracked). - * After a restart, NetworkManager would no longer remember which rules were added - * by us. - * - * That is partially fixed by NetworkManager taking over the rules that it - * actively configures (see %NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */ - ConfigState config_state; -} TrackObjData; - -typedef struct { - gconstpointer user_tag; - CList user_tag_lst_head; -} TrackUserTagData; - -/*****************************************************************************/ - -static void _track_data_untrack(NMPRouteManager *self, - TrackData *track_data, - gboolean remove_user_tag_data, - gboolean make_owned_by_us); - -/*****************************************************************************/ - -static CList * -_by_obj_lst_head(NMPRouteManager *self, NMPObjectType obj_type) -{ - G_STATIC_ASSERT(G_N_ELEMENTS(self->by_obj_lst_heads) == 3); - - switch (obj_type) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - return &self->by_obj_lst_heads[0]; - case NMP_OBJECT_TYPE_IP6_ROUTE: - return &self->by_obj_lst_heads[1]; - case NMP_OBJECT_TYPE_ROUTING_RULE: - return &self->by_obj_lst_heads[2]; - default: - return nm_assert_unreachable_val(NULL); - } -} - -/*****************************************************************************/ - -static void -_track_data_assert(const TrackData *track_data, gboolean linked) -{ - nm_assert(track_data); - nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(track_data->obj), - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_ROUTING_RULE)); - nm_assert(nmp_object_is_visible(track_data->obj)); - nm_assert(track_data->user_tag); - nm_assert(!linked || !c_list_is_empty(&track_data->obj_lst)); - nm_assert(!linked || !c_list_is_empty(&track_data->user_tag_lst)); -} - -static guint -_track_data_hash(gconstpointer data) -{ - const TrackData *track_data = data; - NMHashState h; - - _track_data_assert(track_data, FALSE); - - nm_hash_init(&h, 269297543u); - nmp_object_id_hash_update(track_data->obj, &h); - nm_hash_update_val(&h, track_data->user_tag); - return nm_hash_complete(&h); -} - -static gboolean -_track_data_equal(gconstpointer data_a, gconstpointer data_b) -{ - const TrackData *track_data_a = data_a; - const TrackData *track_data_b = data_b; - - _track_data_assert(track_data_a, FALSE); - _track_data_assert(track_data_b, FALSE); - - return track_data_a->user_tag == track_data_b->user_tag - && nmp_object_id_equal(track_data_a->obj, track_data_b->obj); -} - -static void -_track_data_destroy(gpointer data) -{ - TrackData *track_data = data; - - _track_data_assert(track_data, FALSE); - - c_list_unlink_stale(&track_data->obj_lst); - c_list_unlink_stale(&track_data->user_tag_lst); - nmp_object_unref(track_data->obj); - nm_g_slice_free(track_data); -} - -static const TrackData * -_track_obj_data_get_best_data(TrackObjData *obj_data) -{ - TrackData *track_data; - const TrackData *td_best = NULL; - - c_list_for_each_entry (track_data, &obj_data->obj_lst_head, obj_lst) { - _track_data_assert(track_data, TRUE); - - if (td_best) { - if (td_best->track_priority_val > track_data->track_priority_val) - continue; - if (td_best->track_priority_val == track_data->track_priority_val) { - if (td_best->track_priority_present || !track_data->track_priority_present) { - /* if the priorities are identical, then "present" wins over - * "!present" (absent). */ - continue; - } - } - } - - td_best = track_data; - } - - return td_best; -} - -static guint -_track_obj_data_hash(gconstpointer data) -{ - const TrackObjData *obj_data = data; - - return nmp_object_id_hash(obj_data->obj); -} - -static gboolean -_track_obj_data_equal(gconstpointer data_a, gconstpointer data_b) -{ - const TrackObjData *obj_data_a = data_a; - const TrackObjData *obj_data_b = data_b; - - return nmp_object_id_equal(obj_data_a->obj, obj_data_b->obj); -} - -static void -_track_obj_data_destroy(gpointer data) -{ - TrackObjData *obj_data = data; - - c_list_unlink_stale(&obj_data->obj_lst_head); - c_list_unlink_stale(&obj_data->by_obj_lst); - nmp_object_unref(obj_data->obj); - nm_g_slice_free(obj_data); -} - -static void -_track_user_tag_data_destroy(gpointer data) -{ - TrackUserTagData *user_tag_data = data; - - c_list_unlink_stale(&user_tag_data->user_tag_lst_head); - nm_g_slice_free(user_tag_data); -} - -static TrackData * -_track_data_lookup(GHashTable *by_data, const NMPObject *obj, gconstpointer user_tag) -{ - TrackData track_data_needle = { - .obj = obj, - .user_tag = user_tag, - }; - - return g_hash_table_lookup(by_data, &track_data_needle); -} - -/*****************************************************************************/ - -/** - * nmp_route_manager_track: - * @self: the #NMPRouteManager instance - * @obj_type: the NMPObjectType of @obj that we are tracking. - * @obj: the NMPlatformObject (of type NMPObjectType) to track. Usually - * a #NMPlatformRoutingRule, #NMPlatformIP4Route or #NMPlatformIP6Route - * pointer. - * @track_priority: the priority for tracking the rule. Note that - * negative values indicate a forced absence of the rule. Priorities - * are compared with their absolute values (with higher absolute - * value being more important). For example, if you track the same - * rule twice, once with priority -5 and +10, then the rule is - * present (because the positive number is more important). - * The special value 0 indicates weakly-tracked rules. - * @user_tag: the tag associated with tracking this rule. The same tag - * must be used to untrack the rule later. - * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag - * for the same rule. Note that this is different from a plain nmp_route_manager_untrack_rule(), - * because it enforces ownership of the now tracked rule. On the other hand, - * a plain nmp_route_manager_untrack_rule() merely forgets about the tracking. - * The purpose here is to set this to %NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. - * - * Returns: %TRUE, if something changed. - */ -gboolean -nmp_route_manager_track(NMPRouteManager *self, - NMPObjectType obj_type, - gconstpointer obj, - gint32 track_priority, - gconstpointer user_tag, - gconstpointer user_tag_untrack) -{ - NMPObject obj_stack; - const NMPObject *p_obj_stack; - TrackData *track_data; - TrackObjData *obj_data; - TrackUserTagData *user_tag_data; - gboolean changed = FALSE; - gboolean changed_untrack = FALSE; - guint32 track_priority_val; - gboolean track_priority_present; - - g_return_val_if_fail(NMP_IS_ROUTE_MANAGER(self), FALSE); - g_return_val_if_fail(obj, FALSE); - g_return_val_if_fail(user_tag, FALSE); - - /* The route must not be tied to an interface. We can only handle here - * blackhole/unreachable/prohibit route types. */ - g_return_val_if_fail( - obj_type == NMP_OBJECT_TYPE_ROUTING_RULE - || (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) - && ((const NMPlatformIPRoute *) obj)->ifindex == 0), - FALSE); - - nm_assert(track_priority != G_MININT32); - - p_obj_stack = nmp_object_stackinit(&obj_stack, obj_type, obj); - - nm_assert(nmp_object_is_visible(p_obj_stack)); - - if (track_priority >= 0) { - track_priority_val = track_priority; - track_priority_present = TRUE; - } else { - track_priority_val = -track_priority; - track_priority_present = FALSE; - } - - track_data = _track_data_lookup(self->by_data, p_obj_stack, user_tag); - - if (!track_data) { - track_data = g_slice_new(TrackData); - *track_data = (TrackData){ - .obj = nm_dedup_multi_index_obj_intern(nm_platform_get_multi_idx(self->platform), - p_obj_stack), - .user_tag = user_tag, - .track_priority_val = track_priority_val, - .track_priority_present = track_priority_present, - .dirty = FALSE, - }; - g_hash_table_add(self->by_data, track_data); - - obj_data = g_hash_table_lookup(self->by_obj, &track_data->obj); - if (!obj_data) { - obj_data = g_slice_new(TrackObjData); - *obj_data = (TrackObjData){ - .obj = nmp_object_ref(track_data->obj), - .obj_lst_head = C_LIST_INIT(obj_data->obj_lst_head), - .config_state = CONFIG_STATE_NONE, - }; - g_hash_table_add(self->by_obj, obj_data); - c_list_link_tail(_by_obj_lst_head(self, obj_type), &obj_data->by_obj_lst); - } - c_list_link_tail(&obj_data->obj_lst_head, &track_data->obj_lst); - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &track_data->user_tag); - if (!user_tag_data) { - user_tag_data = g_slice_new(TrackUserTagData); - *user_tag_data = (TrackUserTagData){ - .user_tag = user_tag, - .user_tag_lst_head = C_LIST_INIT(user_tag_data->user_tag_lst_head), - }; - g_hash_table_add(self->by_user_tag, user_tag_data); - } - c_list_link_tail(&user_tag_data->user_tag_lst_head, &track_data->user_tag_lst); - changed = TRUE; - } else { - track_data->dirty = FALSE; - if (track_data->track_priority_val != track_priority_val - || track_data->track_priority_present != track_priority_present) { - track_data->track_priority_val = track_priority_val; - track_data->track_priority_present = track_priority_present; - changed = TRUE; - } - } - - if (user_tag_untrack) { - if (user_tag != user_tag_untrack) { - TrackData *track_data_untrack; - - track_data_untrack = _track_data_lookup(self->by_data, p_obj_stack, user_tag_untrack); - if (track_data_untrack) { - _track_data_untrack(self, track_data_untrack, FALSE, TRUE); - changed_untrack = TRUE; - } - } else - nm_assert_not_reached(); - } - - _track_data_assert(track_data, TRUE); - - if (changed) { - char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - - _LOGD( - "track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", - NM_HASH_OBFUSCATE_PTR(track_data->user_tag), - (track_data->track_priority_val == 0 - ? "" - : (track_data->track_priority_present ? "+" : "-")), - (guint) track_data->track_priority_val, - NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - } - - return changed || changed_untrack; -} - -static void -_track_data_untrack(NMPRouteManager *self, - TrackData *track_data, - gboolean remove_user_tag_data, - gboolean make_owned_by_us) -{ - char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - TrackObjData *obj_data; - - nm_assert(NMP_IS_ROUTE_MANAGER(self)); - _track_data_assert(track_data, TRUE); - nm_assert(self->by_data); - nm_assert(g_hash_table_lookup(self->by_data, track_data) == track_data); - - _LOGD("untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] %s \"%s\"", - NM_HASH_OBFUSCATE_PTR(track_data->user_tag), - NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - -#if NM_MORE_ASSERTS - { - TrackUserTagData *user_tag_data; - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &track_data->user_tag); - nm_assert(user_tag_data); - nm_assert(c_list_contains(&user_tag_data->user_tag_lst_head, &track_data->user_tag_lst)); - } -#endif - - nm_assert(!c_list_is_empty(&track_data->user_tag_lst)); - - obj_data = g_hash_table_lookup(self->by_obj, &track_data->obj); - nm_assert(obj_data); - nm_assert(c_list_contains(&obj_data->obj_lst_head, &track_data->obj_lst)); - nm_assert(obj_data == g_hash_table_lookup(self->by_obj, &track_data->obj)); - - if (make_owned_by_us) { - if (obj_data->config_state == CONFIG_STATE_NONE) { - /* we need to mark this entry that it requires a touch on the next - * sync. */ - obj_data->config_state = CONFIG_STATE_OWNED_BY_US; - } - } else if (remove_user_tag_data && c_list_length_is(&track_data->user_tag_lst, 1)) - g_hash_table_remove(self->by_user_tag, &track_data->user_tag); - - /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry - * around for the next sync -- so that we can undo what we did earlier. */ - if (obj_data->config_state == CONFIG_STATE_NONE && c_list_length_is(&track_data->obj_lst, 1)) - g_hash_table_remove(self->by_obj, &track_data->obj); - - g_hash_table_remove(self->by_data, track_data); -} - -gboolean -nmp_route_manager_untrack(NMPRouteManager *self, - NMPObjectType obj_type, - gconstpointer obj, - gconstpointer user_tag) -{ - NMPObject obj_stack; - const NMPObject *p_obj_stack; - TrackData *track_data; - gboolean changed = FALSE; - - g_return_val_if_fail(NMP_IS_ROUTE_MANAGER(self), FALSE); - nm_assert(NM_IN_SET(obj_type, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_ROUTING_RULE)); - g_return_val_if_fail(obj, FALSE); - g_return_val_if_fail(user_tag, FALSE); - - p_obj_stack = nmp_object_stackinit(&obj_stack, obj_type, obj); - - nm_assert(nmp_object_is_visible(p_obj_stack)); - - track_data = _track_data_lookup(self->by_data, p_obj_stack, user_tag); - if (track_data) { - _track_data_untrack(self, track_data, TRUE, FALSE); - changed = TRUE; - } - - return changed; -} - -void -nmp_route_manager_set_dirty(NMPRouteManager *self, gconstpointer user_tag) -{ - TrackData *track_data; - TrackUserTagData *user_tag_data; - - g_return_if_fail(NMP_IS_ROUTE_MANAGER(self)); - g_return_if_fail(user_tag); - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); - if (!user_tag_data) - return; - - c_list_for_each_entry (track_data, &user_tag_data->user_tag_lst_head, user_tag_lst) - track_data->dirty = TRUE; -} - -gboolean -nmp_route_manager_untrack_all(NMPRouteManager *self, - gconstpointer user_tag, - gboolean all /* or only dirty */, - gboolean make_survivors_dirty) -{ - TrackData *track_data; - TrackData *track_data_safe; - TrackUserTagData *user_tag_data; - gboolean changed = FALSE; - - g_return_val_if_fail(NMP_IS_ROUTE_MANAGER(self), FALSE); - g_return_val_if_fail(user_tag, FALSE); - - user_tag_data = g_hash_table_lookup(self->by_user_tag, &user_tag); - if (!user_tag_data) - return FALSE; - - c_list_for_each_entry_safe (track_data, - track_data_safe, - &user_tag_data->user_tag_lst_head, - user_tag_lst) { - if (all || track_data->dirty) { - _track_data_untrack(self, track_data, FALSE, FALSE); - changed = TRUE; - continue; - } - if (make_survivors_dirty) - track_data->dirty = TRUE; - } - if (c_list_is_empty(&user_tag_data->user_tag_lst_head)) - g_hash_table_remove(self->by_user_tag, user_tag_data); - - return changed; -} - -/*****************************************************************************/ - -void -nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean keep_deleted) -{ - char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; - const NMDedupMultiHeadEntry *pl_head_entry; - NMDedupMultiIter pl_iter; - const NMPObject *plobj; - gs_unref_ptrarray GPtrArray *objs_to_delete = NULL; - TrackObjData *obj_data; - TrackObjData *obj_data_safe; - CList *by_obj_lst_head; - guint i; - const TrackData *td_best; - - g_return_if_fail(NMP_IS_ROUTE_MANAGER(self)); - g_return_if_fail(NM_IN_SET(obj_type, - NMP_OBJECT_TYPE_IP4_ROUTE, - NMP_OBJECT_TYPE_IP6_ROUTE, - NMP_OBJECT_TYPE_ROUTING_RULE)); - - _LOGD("sync %s%s", - nmp_class_from_type(obj_type)->obj_type_name, - keep_deleted ? " (don't remove any)" : ""); - - if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) - pl_head_entry = nm_platform_lookup_obj_type(self->platform, obj_type); - else - pl_head_entry = nm_platform_lookup_object(self->platform, obj_type, 0); - - if (pl_head_entry) { - nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { - obj_data = g_hash_table_lookup(self->by_obj, &plobj); - - if (!obj_data) { - /* this obj is not tracked. It was externally added, hence we - * ignore it. */ - continue; - } - - td_best = _track_obj_data_get_best_data(obj_data); - if (td_best) { - if (td_best->track_priority_present) { - if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) - obj_data->config_state = CONFIG_STATE_ADDED_BY_US; - continue; - } - if (td_best->track_priority_val == 0) { - if (!NM_IN_SET(obj_data->config_state, - CONFIG_STATE_ADDED_BY_US, - CONFIG_STATE_OWNED_BY_US)) { - obj_data->config_state = CONFIG_STATE_NONE; - continue; - } - obj_data->config_state = CONFIG_STATE_NONE; - } - } - - if (keep_deleted) { - _LOGD("forget/leak object added by us: %s \"%s\"", - NMP_OBJECT_GET_CLASS(plobj)->obj_type_name, - nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - continue; - } - - if (!objs_to_delete) - objs_to_delete = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref); - - g_ptr_array_add(objs_to_delete, (gpointer) nmp_object_ref(plobj)); - - obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; - } - } - - if (objs_to_delete) { - for (i = 0; i < objs_to_delete->len; i++) - nm_platform_object_delete(self->platform, objs_to_delete->pdata[i]); - } - - by_obj_lst_head = _by_obj_lst_head(self, obj_type); - - c_list_for_each_entry_safe (obj_data, obj_data_safe, by_obj_lst_head, by_obj_lst) { - nm_assert(NMP_OBJECT_GET_TYPE(obj_data->obj) == obj_type); - - td_best = _track_obj_data_get_best_data(obj_data); - - if (!td_best) { - g_hash_table_remove(self->by_obj, obj_data); - continue; - } - - if (!td_best->track_priority_present) { - if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) - obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; - continue; - } - if (td_best->track_priority_val == 0) { - if (!NM_IN_SET(obj_data->config_state, - CONFIG_STATE_REMOVED_BY_US, - CONFIG_STATE_OWNED_BY_US)) { - obj_data->config_state = CONFIG_STATE_NONE; - continue; - } - obj_data->config_state = CONFIG_STATE_NONE; - } - - plobj = - nm_platform_lookup_obj(self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); - if (plobj) - continue; - - obj_data->config_state = CONFIG_STATE_ADDED_BY_US; - - if (obj_type == NMP_OBJECT_TYPE_ROUTING_RULE) { - nm_platform_routing_rule_add(self->platform, - NMP_NLM_FLAG_ADD, - NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj)); - } else - nm_platform_ip_route_add(self->platform, NMP_NLM_FLAG_APPEND, obj_data->obj); - } -} - -/*****************************************************************************/ - -void -nmp_route_manager_track_rule_from_platform(NMPRouteManager *self, - NMPlatform *platform, - int addr_family, - gint32 tracking_priority, - gconstpointer user_tag) -{ - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject *o; - - g_return_if_fail(NMP_IS_ROUTE_MANAGER(self)); - - if (!platform) - platform = self->platform; - else - g_return_if_fail(NM_IS_PLATFORM(platform)); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - nmp_lookup_init_obj_type(&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); - head_entry = nm_platform_lookup(platform, &lookup); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE(o); - - if (addr_family != AF_UNSPEC && rr->addr_family != addr_family) - continue; - - nmp_route_manager_track_rule(self, rr, tracking_priority, user_tag, NULL); - } -} - -/*****************************************************************************/ - -void -nmp_route_manager_track_rule_default(NMPRouteManager *self, - int addr_family, - gint32 track_priority, - gconstpointer user_tag) -{ - g_return_if_fail(NMP_IS_ROUTE_MANAGER(self)); - - nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6)); - - /* track the default rules. See also `man ip-rule`. */ - - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET)) { - nmp_route_manager_track_rule(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 0, - .table = RT_TABLE_LOCAL, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_route_manager_track_rule(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 32766, - .table = RT_TABLE_MAIN, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_route_manager_track_rule(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET, - .priority = 32767, - .table = RT_TABLE_DEFAULT, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - } - if (NM_IN_SET(addr_family, AF_UNSPEC, AF_INET6)) { - nmp_route_manager_track_rule(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET6, - .priority = 0, - .table = RT_TABLE_LOCAL, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - nmp_route_manager_track_rule(self, - &((NMPlatformRoutingRule){ - .addr_family = AF_INET6, - .priority = 32766, - .table = RT_TABLE_MAIN, - .action = FR_ACT_TO_TBL, - .protocol = RTPROT_KERNEL, - }), - track_priority, - user_tag, - NULL); - } -} - -/*****************************************************************************/ - -NMPRouteManager * -nmp_route_manager_new(NMPlatform *platform) -{ - NMPRouteManager *self; - - g_return_val_if_fail(NM_IS_PLATFORM(platform), NULL); - - G_STATIC_ASSERT_EXPR(G_STRUCT_OFFSET(TrackUserTagData, user_tag) == 0); - - self = g_slice_new(NMPRouteManager); - *self = (NMPRouteManager){ - .ref_count = 1, - .platform = g_object_ref(platform), - .by_data = - g_hash_table_new_full(_track_data_hash, _track_data_equal, NULL, _track_data_destroy), - .by_obj = g_hash_table_new_full(_track_obj_data_hash, - _track_obj_data_equal, - NULL, - _track_obj_data_destroy), - .by_user_tag = g_hash_table_new_full(nm_pdirect_hash, - nm_pdirect_equal, - NULL, - _track_user_tag_data_destroy), - .by_obj_lst_heads[0] = C_LIST_INIT(self->by_obj_lst_heads[0]), - .by_obj_lst_heads[1] = C_LIST_INIT(self->by_obj_lst_heads[1]), - .by_obj_lst_heads[2] = C_LIST_INIT(self->by_obj_lst_heads[2]), - }; - return self; -} - -NMPRouteManager * -nmp_route_manager_ref(NMPRouteManager *self) -{ - g_return_val_if_fail(NMP_IS_ROUTE_MANAGER(self), NULL); - - self->ref_count++; - return self; -} - -void -nmp_route_manager_unref(NMPRouteManager *self) -{ - g_return_if_fail(NMP_IS_ROUTE_MANAGER(self)); - - if (--self->ref_count > 0) - return; - - g_hash_table_destroy(self->by_user_tag); - g_hash_table_destroy(self->by_obj); - g_hash_table_destroy(self->by_data); - nm_assert(c_list_is_empty(&self->by_obj_lst_heads[0])); - nm_assert(c_list_is_empty(&self->by_obj_lst_heads[1])); - nm_assert(c_list_is_empty(&self->by_obj_lst_heads[2])); - g_object_unref(self->platform); - nm_g_slice_free(self); -} diff --git a/src/libnm-platform/nmp-route-manager.h b/src/libnm-platform/nmp-route-manager.h deleted file mode 100644 index 97ec3840..00000000 --- a/src/libnm-platform/nmp-route-manager.h +++ /dev/null @@ -1,79 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#ifndef __NMP_ROUTE_MANAGER_H__ -#define __NMP_ROUTE_MANAGER_H__ - -#include "nm-platform.h" - -/*****************************************************************************/ - -#define NMP_ROUTE_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_route_manager_new) - -typedef struct _NMPRouteManager NMPRouteManager; - -NMPRouteManager *nmp_route_manager_new(NMPlatform *platform); - -NMPRouteManager *nmp_route_manager_ref(NMPRouteManager *self); -void nmp_route_manager_unref(NMPRouteManager *self); - -#define nm_auto_unref_route_manager nm_auto(_nmp_route_manager_unref) -NM_AUTO_DEFINE_FCN0(NMPRouteManager *, _nmp_route_manager_unref, nmp_route_manager_unref); - -gboolean nmp_route_manager_track(NMPRouteManager *self, - NMPObjectType obj_type, - gconstpointer obj, - gint32 track_priority, - gconstpointer user_tag, - gconstpointer user_tag_untrack); - -static inline gboolean -nmp_route_manager_track_rule(NMPRouteManager *self, - const NMPlatformRoutingRule *routing_rule, - gint32 track_priority, - gconstpointer user_tag, - gconstpointer user_tag_untrack) -{ - return nmp_route_manager_track(self, - NMP_OBJECT_TYPE_ROUTING_RULE, - routing_rule, - track_priority, - user_tag, - user_tag_untrack); -} - -void nmp_route_manager_track_rule_default(NMPRouteManager *self, - int addr_family, - gint32 track_priority, - gconstpointer user_tag); - -void nmp_route_manager_track_rule_from_platform(NMPRouteManager *self, - NMPlatform *platform, - int addr_family, - gint32 tracking_priority, - gconstpointer user_tag); - -gboolean nmp_route_manager_untrack(NMPRouteManager *self, - NMPObjectType obj_type, - gconstpointer obj, - gconstpointer user_tag); - -static inline gboolean -nmp_route_manager_untrack_rule(NMPRouteManager *self, - const NMPlatformRoutingRule *routing_rule, - gconstpointer user_tag) -{ - return nmp_route_manager_untrack(self, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule, user_tag); -} - -void nmp_route_manager_set_dirty(NMPRouteManager *self, gconstpointer user_tag); - -gboolean nmp_route_manager_untrack_all(NMPRouteManager *self, - gconstpointer user_tag, - gboolean all /* or only dirty */, - gboolean make_survivors_dirty); - -void nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean keep_deleted); - -/*****************************************************************************/ - -#endif /* __NMP_ROUTE_MANAGER_H__ */ diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c index 9ac69bde..c351f014 100644 --- a/src/libnm-platform/tests/test-nm-platform.c +++ b/src/libnm-platform/tests/test-nm-platform.c @@ -59,7 +59,7 @@ test_use_symbols(void) (void (*)(void)) genlmsg_valid_hdr, (void (*)(void)) genlmsg_parse, (void (*)(void)) genl_ctrl_resolve, - (void (*)(void)) nl_socket_alloc, + (void (*)(void)) nl_socket_new, (void (*)(void)) nl_socket_free, (void (*)(void)) nl_socket_get_fd, (void (*)(void)) nl_socket_get_local_port, @@ -70,9 +70,6 @@ test_use_symbols(void) (void (*)(void)) nl_socket_set_nonblocking, (void (*)(void)) nl_socket_set_buffer_size, (void (*)(void)) nl_socket_add_memberships, - (void (*)(void)) nl_socket_set_ext_ack, - (void (*)(void)) nl_socket_disable_msg_peek, - (void (*)(void)) nl_connect, (void (*)(void)) nl_wait_for_ack, (void (*)(void)) nl_recvmsgs, (void (*)(void)) nl_sendmsg, diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c index 475b8a39..37edd928 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c @@ -9,14 +9,13 @@ #include "nm-wifi-utils-nl80211.h" +#include "libnm-std-aux/nm-linux-compat.h" + #include <sys/ioctl.h> -#include <net/ethernet.h> #include <unistd.h> #include <linux/nl80211.h> #include <linux/if.h> -#include "linux-headers/nl80211-vnd-intel.h" - #include "libnm-log-core/nm-logging.h" #include "libnm-platform/nm-netlink.h" #include "nm-wifi-utils-private.h" @@ -46,9 +45,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; @@ -59,7 +58,7 @@ typedef struct { G_DEFINE_TYPE(NMWifiUtilsNl80211, nm_wifi_utils_nl80211, NM_TYPE_WIFI_UTILS) static int -ack_handler(struct nl_msg *msg, void *arg) +ack_handler(const struct nl_msg *msg, void *arg) { int *done = arg; *done = 1; @@ -67,7 +66,7 @@ ack_handler(struct nl_msg *msg, void *arg) } static int -finish_handler(struct nl_msg *msg, void *arg) +finish_handler(const struct nl_msg *msg, void *arg) { int *done = arg; *done = 1; @@ -75,7 +74,7 @@ finish_handler(struct nl_msg *msg, void *arg) } static int -error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +error_handler(const struct sockaddr_nl *nla, const struct nlmsgerr *err, void *arg) { int *done = arg; *done = err->error; @@ -83,12 +82,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,15 +98,15 @@ 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 nl80211_send_and_recv(NMWifiUtilsNl80211 *self, struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), + int (*valid_handler)(const struct nl_msg *, void *), void *valid_data) { int err; @@ -168,7 +167,7 @@ struct nl80211_iface_info { }; static int -nl80211_iface_info_handler(struct nl_msg *msg, void *arg) +nl80211_iface_info_handler(const struct nl_msg *msg, void *arg) { struct nl80211_iface_info *info = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -270,7 +269,7 @@ nla_put_failure: } static int -nl80211_get_wake_on_wlan_handler(struct nl_msg *msg, void *arg) +nl80211_get_wake_on_wlan_handler(const struct nl_msg *msg, void *arg) { _NMSettingWirelessWakeOnWLan *wowl = arg; struct nlattr *attrs[NL80211_ATTR_MAX + 1]; @@ -423,7 +422,7 @@ struct nl80211_station_info { }; static int -nl80211_station_dump_handler(struct nl_msg *msg, void *arg) +nl80211_station_dump_handler(const struct nl_msg *msg, void *arg) { static const struct nla_policy stats_policy[] = { [NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32}, @@ -577,7 +576,7 @@ struct nl80211_device_info { #define WLAN_CIPHER_SUITE_SMS4 0x00147201 static int -nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) +nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg) { static const struct nla_policy freq_policy[] = { [NL80211_FREQUENCY_ATTR_FREQ] = {.type = NLA_U32}, @@ -817,7 +816,7 @@ struct nl80211_csme_conn_info { }; static int -nl80211_csme_conn_event_handler(struct nl_msg *msg, void *arg) +nl80211_csme_conn_event_handler(const struct nl_msg *msg, void *arg) { struct nl80211_csme_conn_info *info = arg; NMPlatformCsmeConnInfo *out_conn_info = info->conn_info; @@ -945,7 +944,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 +953,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 4a633307..4783c572 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 08a8ec4f..0238b74a 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 0d30c1a1..fd636418 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -7,8 +7,6 @@ #ifndef __WIFI_UTILS_H__ #define __WIFI_UTILS_H__ -#include <net/ethernet.h> - #include "libnm-platform/nm-netlink.h" #include "libnm-base/nm-base.h" @@ -27,7 +25,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 d9bbd0f0..675efe5a 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 { @@ -51,7 +51,7 @@ G_DEFINE_TYPE(NMWpanUtils, nm_wpan_utils, G_TYPE_OBJECT) /*****************************************************************************/ static int -ack_handler(struct nl_msg *msg, void *arg) +ack_handler(const struct nl_msg *msg, void *arg) { int *done = arg; *done = 1; @@ -59,7 +59,7 @@ ack_handler(struct nl_msg *msg, void *arg) } static int -finish_handler(struct nl_msg *msg, void *arg) +finish_handler(const struct nl_msg *msg, void *arg) { int *done = arg; *done = 1; @@ -67,7 +67,7 @@ finish_handler(struct nl_msg *msg, void *arg) } static int -error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +error_handler(const struct sockaddr_nl *nla, const struct nlmsgerr *err, void *arg) { int *done = arg; *done = err->error; @@ -75,12 +75,13 @@ 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); + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, genl_family_id, 0, flags, cmd, 0)) + goto nla_put_failure; NLA_PUT_U32(msg, NL802154_ATTR_IFINDEX, ifindex); return g_steal_pointer(&msg); @@ -89,15 +90,15 @@ 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 nl802154_send_and_recv(NMWpanUtils *self, struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), + int (*valid_handler)(const struct nl_msg *, void *), void *valid_data) { int err; @@ -143,7 +144,7 @@ struct nl802154_interface { }; static int -nl802154_get_interface_handler(struct nl_msg *msg, void *arg) +nl802154_get_interface_handler(const struct nl_msg *msg, void *arg) { static const struct nla_policy nl802154_policy[] = { [NL802154_ATTR_PAN_ID] = {.type = NLA_U16}, @@ -264,7 +265,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 +274,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 6130c41e..eea5587d 100644 --- a/src/libnm-platform/wpan/nm-wpan-utils.h +++ b/src/libnm-platform/wpan/nm-wpan-utils.h @@ -6,8 +6,6 @@ #ifndef __WPAN_UTILS_H__ #define __WPAN_UTILS_H__ -#include <net/ethernet.h> - #include "libnm-platform/nm-netlink.h" typedef struct NMWpanUtils NMWpanUtils; @@ -23,7 +21,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); |