diff options
Diffstat (limited to 'src/platform/nm-linux-platform.c')
| -rw-r--r-- | src/platform/nm-linux-platform.c | 2379 |
1 files changed, 1158 insertions, 1221 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6b84c185..c4c93ed3 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -48,6 +48,7 @@ #include "nmp-object.h" #include "nmp-netns.h" #include "nm-platform-utils.h" +#include "nm-platform-private.h" #include "wifi/wifi-utils.h" #include "wifi/wifi-utils-wext.h" #include "nm-utils/unaligned.h" @@ -103,6 +104,14 @@ #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) #endif + +static const gboolean RTA_PREF_SUPPORTED_AT_COMPILETIME = (RTA_MAX >= 20 /* RTA_PREF */); + +G_STATIC_ASSERT (RTA_MAX == (__RTA_MAX - 1)); +#define RTA_PREF 20 +#undef RTA_MAX +#define RTA_MAX (MAX ((__RTA_MAX - 1), RTA_PREF)) + #ifndef MACVLAN_FLAG_NOPROMISC #define MACVLAN_FLAG_NOPROMISC 1 #endif @@ -197,6 +206,21 @@ typedef enum { INFINIBAND_ACTION_DELETE_CHILD, } InfinibandAction; +typedef enum { + CHANGE_LINK_TYPE_UNSPEC, + CHANGE_LINK_TYPE_SET_MTU, + CHANGE_LINK_TYPE_SET_ADDRESS, +} ChangeLinkType; + +typedef struct { + union { + struct { + gconstpointer address; + gsize length; + } set_address; + }; +} ChangeLinkData; + enum { DELAYED_ACTION_IDX_REFRESH_ALL_LINKS, DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES, @@ -255,13 +279,25 @@ static void delayed_action_schedule (NMPlatform *platform, DelayedActionType act static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); 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_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); -static void cache_prune_candidates_prune (NMPlatform *platform); +static void cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + 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 void ASSERT_NETNS_CURRENT (NMPlatform *platform); /*****************************************************************************/ +static NMPlatformError +wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) +{ + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return NM_PLATFORM_ERROR_SUCCESS; + if (seq_result < 0) + return (NMPlatformError) seq_result; + return NM_PLATFORM_ERROR_NETLINK; +} + static const char * wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) { @@ -287,35 +323,117 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g return buf0; } -/****************************************************************** +/***************************************************************************** * Support IFLA_INET6_ADDR_GEN_MODE - ******************************************************************/ + *****************************************************************************/ static int _support_user_ipv6ll = 0; #define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +static void +_support_user_ipv6ll_detect (struct nlattr **tb) +{ + gboolean supported; + + nm_assert (_support_user_ipv6ll_still_undecided ()); + + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + _support_user_ipv6ll = supported ? 1 : -1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", + supported ? "detected" : "not detected"); +} + static gboolean _support_user_ipv6ll_get (void) { if (_support_user_ipv6ll_still_undecided ()) { - _support_user_ipv6ll = -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume no support"); - return FALSE; + _support_user_ipv6ll = 1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); } - return _support_user_ipv6ll > 0; + return _support_user_ipv6ll >= 0; +} + +/***************************************************************************** + * extended IFA_FLAGS support + *****************************************************************************/ +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + gboolean support; + + nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); + nm_assert (msg); + + msg_hdr = nlmsg_hdr (msg); + + nm_assert (msg_hdr && msg_hdr->nlmsg_type == RTM_NEWADDR); + + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if (NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6)) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + support = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); + _support_kernel_extended_ifa_flags = support ? 1 : -1; + _LOG2D ("kernel-support: extended-ifa-flags: %s", support ? "detected" : "not detected"); } +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); + _support_kernel_extended_ifa_flags = 1; + } + return _support_kernel_extended_ifa_flags >= 0; +} + +/***************************************************************************** + * Support RTA_PREF + *****************************************************************************/ + +static int _support_rta_pref = 0; +#define _support_rta_pref_still_undecided() (G_UNLIKELY (_support_rta_pref == 0)) + static void -_support_user_ipv6ll_detect (struct nlattr **tb) +_support_rta_pref_detect (struct nlattr **tb) { - if (_support_user_ipv6ll_still_undecided ()) { - gboolean supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + gboolean supported; + + nm_assert (_support_rta_pref_still_undecided ()); - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); + /* RTA_PREF was added in kernel 4.1, dated 21 June, 2015. */ + supported = !!tb[RTA_PREF]; + _support_rta_pref = supported ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + supported ? "detected" : "not detected"); +} + +static gboolean +_support_rta_pref_get (void) +{ + if (_support_rta_pref_still_undecided ()) { + /* if we couldn't detect support, we fallback on compile-time check, whether + * RTA_PREF is present in the kernel headers. */ + _support_rta_pref = RTA_PREF_SUPPORTED_AT_COMPILETIME ? 1 : -1; + _LOG2D ("kernel-support: RTA_PREF: ability to set router preference for IPv6 routes: %s", + RTA_PREF_SUPPORTED_AT_COMPILETIME ? "assume support" : "assume no support"); } + return _support_rta_pref >= 0; } /****************************************************************** @@ -377,6 +495,7 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_WWAN_NET, "wwan", NULL, "wwan" }, { NM_LINK_TYPE_WIMAX, "wimax", "wimax", "wimax" }, + { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_DUMMY, "dummy", "dummy", NULL }, { NM_LINK_TYPE_GRE, "gre", "gre", NULL }, { NM_LINK_TYPE_GRETAP, "gretap", "gretap", NULL }, @@ -388,13 +507,13 @@ static const LinkDesc linktypes[] = { { NM_LINK_TYPE_MACVLAN, "macvlan", "macvlan", NULL }, { NM_LINK_TYPE_MACVTAP, "macvtap", "macvtap", NULL }, { NM_LINK_TYPE_OPENVSWITCH, "openvswitch", "openvswitch", NULL }, + { NM_LINK_TYPE_PPP, "ppp", NULL, "ppp" }, { NM_LINK_TYPE_SIT, "sit", "sit", NULL }, { NM_LINK_TYPE_TAP, "tap", NULL, NULL }, { NM_LINK_TYPE_TUN, "tun", NULL, NULL }, { NM_LINK_TYPE_VETH, "veth", "veth", NULL }, { NM_LINK_TYPE_VLAN, "vlan", "vlan", "vlan" }, { NM_LINK_TYPE_VXLAN, "vxlan", "vxlan", "vxlan" }, - { NM_LINK_TYPE_BNEP, "bluetooth", NULL, "bluetooth" }, { NM_LINK_TYPE_BRIDGE, "bridge", "bridge", "bridge" }, { NM_LINK_TYPE_BOND, "bond", "bond", "bond" }, @@ -628,7 +747,7 @@ _linktype_get_type (NMPlatform *platform, { guint i; - ASSERT_NETNS_CURRENT (platform); + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (platform); nm_assert (ifname); if (completed_from_cache) { @@ -693,6 +812,8 @@ _linktype_get_type (NMPlatform *platform, return NM_LINK_TYPE_SIT; else if (arptype == ARPHRD_TUNNEL6) return NM_LINK_TYPE_IP6TNL; + else if (arptype == ARPHRD_PPP) + return NM_LINK_TYPE_PPP; { NMPUtilsEthtoolDriverInfo driver_info; @@ -762,6 +883,10 @@ _linktype_get_type (NMPlatform *platform, * aside from the DEVTYPE. */ if (!g_strcmp0 (devtype, "gadget")) return NM_LINK_TYPE_ETHERNET; + + /* Distributed Switch Architecture switch chips */ + if (!g_strcmp0 (devtype, "dsa")) + return NM_LINK_TYPE_ETHERNET; } } @@ -772,32 +897,149 @@ _linktype_get_type (NMPlatform *platform, * libnl unility functions and wrappers ******************************************************************/ -#define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +/* copied from iproute2's addattr_l(). */ +static gboolean +_nl_addattr_l (struct nlmsghdr *n, + int maxlen, + int type, + const void *data, + int alen) +{ + int len = RTA_LENGTH (alen); + struct rtattr *rta; + + if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) + return FALSE; + + rta = NLMSG_TAIL (n); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); + return TRUE; +} + static void _nm_auto_nl_msg_cleanup (void *ptr) { nlmsg_free (*((struct nl_msg **) ptr)); } +#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) static const char * -_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +_nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len) { - const char *str_type = NULL; + const char *b; + const char *s; + guint flags, flags_before; + const char *prefix; - switch (type) { - case RTM_NEWLINK: str_type = "NEWLINK"; break; - case RTM_DELLINK: str_type = "DELLINK"; break; - case RTM_NEWADDR: str_type = "NEWADDR"; break; - case RTM_DELADDR: str_type = "DELADDR"; break; - case RTM_NEWROUTE: str_type = "NEWROUTE"; break; - case RTM_DELROUTE: str_type = "DELROUTE"; break; + nm_utils_to_string_buffer_init (&buf, &len); + b = buf; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: s = "NEWLINK"; break; + case RTM_DELLINK: s = "DELLINK"; break; + case RTM_NEWADDR: s = "NEWADDR"; break; + case RTM_DELADDR: s = "DELADDR"; break; + case RTM_NEWROUTE: s = "NEWROUTE"; break; + case RTM_DELROUTE: s = "DELROUTE"; break; + default: s = NULL; break; } - if (str_type) - g_strlcpy (buf, str_type, len); + + if (s) + nm_utils_strbuf_append (&buf, &len, "RTM_%s", s); else - g_snprintf (buf, len, "(%d)", type); - return buf; + nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); + + flags = hdr->nlmsg_flags; + + if (!flags) { + nm_utils_strbuf_append_str (&buf, &len, ", flags 0"); + goto flags_done; + } + +#define _F(f, n) \ + G_STMT_START { \ + if (NM_FLAGS_ALL (flags, f)) { \ + flags &= ~(f); \ + nm_utils_strbuf_append (&buf, &len, "%s%s", prefix, n); \ + if (!flags) \ + goto flags_done; \ + prefix = ","; \ + } \ + } G_STMT_END + + prefix = ", flags "; + flags_before = flags; + _F (NLM_F_REQUEST, "request"); + _F (NLM_F_MULTI, "multi"); + _F (NLM_F_ACK, "ack"); + _F (NLM_F_ECHO, "echo"); + _F (NLM_F_DUMP_INTR, "dump_intr"); + _F (0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); + + if (flags_before != flags) + prefix = ";"; + + switch (hdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_NEWROUTE: + _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: + _F (NLM_F_DUMP, "dump"); + _F (NLM_F_ROOT, "root"); + _F (NLM_F_MATCH, "match"); + _F (NLM_F_ATOMIC, "atomic"); + break; + } + +#undef _F + + if (flags_before != flags) + prefix = ";"; + nm_utils_strbuf_append (&buf, &len, "%s0x%04x", prefix, flags); + +flags_done: + + nm_utils_strbuf_append (&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); + + return b; +} + +static int +_nl_nla_parse (struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + const struct nla_policy *policy) +{ + return nla_parse (tb, maxtype, head, len, (struct nla_policy *) policy); +} +#define nla_parse(...) _nl_nla_parse(__VA_ARGS__) + +static int +_nl_nlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, const struct nla_policy *policy) +{ + return nlmsg_parse (nlh, hdrlen, tb, maxtype, (struct nla_policy *) policy); } +#define nlmsg_parse(...) _nl_nlmsg_parse(__VA_ARGS__) + +static int +_nl_nla_parse_nested (struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy) +{ + return nla_parse_nested (tb, maxtype, nla, (struct nla_policy *) policy); +} +#define nla_parse_nested(...) _nl_nla_parse_nested(__VA_ARGS__) /****************************************************************** * NMPObject/netlink functions @@ -826,7 +1068,7 @@ _parse_af_inet6 (NMPlatform *platform, guint8 *out_addr_gen_mode_inv, gboolean *out_addr_gen_mode_valid) { - static struct nla_policy policy[IFLA_INET6_MAX+1] = { + static const struct nla_policy policy[IFLA_INET6_MAX+1] = { [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, [IFLA_INET6_CACHEINFO] = { .minlen = nm_offsetofend (struct ifla_cacheinfo, retrans_time) }, [IFLA_INET6_CONF] = { .minlen = 4 }, @@ -862,7 +1104,8 @@ _parse_af_inet6 (NMPlatform *platform, /* Hack to detect support addrgenmode of the kernel. We only parse * netlink messages that we receive from kernel, hence this check * is valid. */ - _support_user_ipv6ll_detect (tb); + if (_support_user_ipv6ll_still_undecided ()) + _support_user_ipv6ll_detect (tb); if (tb[IFLA_INET6_ADDR_GEN_MODE]) { i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); @@ -892,7 +1135,7 @@ errout: static NMPObject * _parse_lnk_gre (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_GRE_MAX + 1] = { + static const struct nla_policy policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_LINK] = { .type = NLA_U32 }, [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, @@ -952,7 +1195,7 @@ _parse_lnk_gre (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, @@ -998,7 +1241,7 @@ _parse_lnk_infiniband (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_UNSPEC, .minlen = sizeof (struct in6_addr)}, @@ -1051,7 +1294,7 @@ _parse_lnk_ip6tnl (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_ipip (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1089,7 +1332,7 @@ _parse_lnk_ipip (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, }; @@ -1132,7 +1375,7 @@ _parse_lnk_macvlan (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_macsec (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[__IFLA_MACSEC_MAX] = { + static const struct nla_policy policy[__IFLA_MACSEC_MAX] = { [IFLA_MACSEC_SCI] = { .type = NLA_U64 }, [IFLA_MACSEC_ICV_LEN] = { .type = NLA_U8 }, [IFLA_MACSEC_CIPHER_SUITE] = { .type = NLA_U64 }, @@ -1182,7 +1425,7 @@ _parse_lnk_macsec (const char *kind, struct nlattr *info_data) static NMPObject * _parse_lnk_sit (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { + static const struct nla_policy policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 }, [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 }, @@ -1284,7 +1527,7 @@ _vlan_qos_mapping_from_nla (struct nlattr *nlattr, static NMPObject * _parse_lnk_vlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VLAN_MAX+1] = { + static const struct nla_policy policy[IFLA_VLAN_MAX+1] = { [IFLA_VLAN_ID] = { .type = NLA_U16 }, [IFLA_VLAN_FLAGS] = { .minlen = nm_offsetofend (struct ifla_vlan_flags, flags) }, [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, @@ -1370,7 +1613,7 @@ struct nm_ifla_vxlan_port_range { static NMPObject * _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) { - static struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { + static const struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_ID] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, @@ -1460,7 +1703,7 @@ _parse_lnk_vxlan (const char *kind, struct nlattr *info_data) static NMPObject * _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFLA_MAX+1] = { + static const struct nla_policy policy[IFLA_MAX+1] = { [IFLA_IFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFLA_MTU] = { .type = NLA_U32 }, @@ -1488,7 +1731,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr [IFLA_NET_NS_PID] = { .type = NLA_U32 }, [IFLA_NET_NS_FD] = { .type = NLA_U32 }, }; - static struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { + static const struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { [IFLA_INFO_KIND] = { .type = NLA_STRING }, [IFLA_INFO_DATA] = { .type = NLA_NESTED }, [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, @@ -1514,6 +1757,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr return NULL; ifi = nlmsg_data(nlh); + if (ifi->ifi_family != AF_UNSPEC) + return NULL; + obj = nmp_object_new_link (ifi->ifi_index); if (id_only) @@ -1680,7 +1926,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr * Also, sometimes the info-data is missing for updates. In this case * we want to keep the previously received lnk_data. */ nmp_object_unref (lnk_data); - lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + lnk_data = (NMPObject *) nmp_object_ref (link_cached->_link.netlink.lnk); } if (address_complete_from_cache) obj->link.addr = link_cached->link.addr; @@ -1711,7 +1957,7 @@ errout: static NMPObject * _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[IFA_MAX+1] = { + static const struct nla_policy policy[IFA_MAX+1] = { [IFA_LABEL] = { .type = NLA_STRING, .maxlen = IFNAMSIZ }, [IFA_CACHEINFO] = { .minlen = nm_offsetofend (struct ifa_cacheinfo, tstamp) }, @@ -1733,7 +1979,7 @@ _new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) goto errout; is_v4 = ifa->ifa_family == AF_INET; - err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, policy); + err = nlmsg_parse (nlh, sizeof(*ifa), tb, IFA_MAX, policy); if (err < 0) goto errout; @@ -1826,10 +2072,12 @@ errout: static NMPObject * _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) { - static struct nla_policy policy[RTA_MAX+1] = { + static const struct nla_policy policy[RTA_MAX+1] = { + [RTA_TABLE] = { .type = NLA_U32 }, [RTA_IIF] = { .type = NLA_U32 }, [RTA_OIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_PREF] = { .type = NLA_U8 }, [RTA_FLOW] = { .type = NLA_U32 }, [RTA_CACHEINFO] = { .minlen = nm_offsetofend (struct rta_cacheinfo, rta_tsage) }, [RTA_METRICS] = { .type = NLA_NESTED }, @@ -1849,7 +2097,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) } nh; guint32 mss; guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0; - guint32 table; if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) return NULL; @@ -1869,12 +2116,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) if (err < 0) goto errout; - table = tb[RTA_TABLE] - ? nla_get_u32 (tb[RTA_TABLE]) - : (guint32) rtm->rtm_table; - if (table != RT_TABLE_MAIN) - goto errout; - /*****************************************************************/ is_v4 = rtm->rtm_family == AF_INET; @@ -1928,7 +2169,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) || tb[RTA_GATEWAY] || tb[RTA_FLOW]) { int ifindex = 0; - NMIPAddr gateway = NMIPAddrInit; + NMIPAddr gateway = { }; if (tb[RTA_OIF]) ifindex = nla_get_u32 (tb[RTA_OIF]); @@ -1956,7 +2197,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) mss = 0; if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; - static struct nla_policy rtax_policy[RTAX_MAX + 1] = { + static const struct nla_policy rtax_policy[RTAX_MAX + 1] = { [RTAX_LOCK] = { .type = NLA_U32 }, [RTAX_ADVMSS] = { .type = NLA_U32 }, [RTAX_WINDOW] = { .type = NLA_U32 }, @@ -1990,6 +2231,10 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip_route.table_coerced = nm_platform_route_table_coerce ( tb[RTA_TABLE] + ? nla_get_u32 (tb[RTA_TABLE]) + : (guint32) rtm->rtm_table); + obj->ip_route.ifindex = nh.ifindex; if (_check_addr_or_errout (tb, RTA_DST, addr_len)) @@ -2015,9 +2260,13 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len); } - if (!is_v4 && tb[RTA_SRC]) { - _check_addr_or_errout (tb, RTA_SRC, addr_len); - memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + if (is_v4) + obj->ip4_route.tos = rtm->rtm_tos; + else { + if (tb[RTA_SRC]) { + _check_addr_or_errout (tb, RTA_SRC, addr_len); + memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len); + } obj->ip6_route.src_plen = rtm->rtm_src_len; } @@ -2027,12 +2276,20 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj->ip_route.initcwnd = initcwnd; obj->ip_route.initrwnd = initrwnd; obj->ip_route.mtu = mtu; - obj->ip_route.tos = rtm->rtm_tos; - 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_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_mtu = NM_FLAGS_HAS (lock, 1 << RTAX_MTU); + + if (!is_v4) { + /* Detect support for RTA_PREF by inspecting the netlink message. */ + if (_support_rta_pref_still_undecided ()) + _support_rta_pref_detect (tb); + + if (tb[RTA_PREF]) + obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]); + } if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) { /* we must not straight way reject cloned routes, because we might have cached @@ -2135,12 +2392,14 @@ nla_put_failure: static gboolean _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, - NMLinkType link_type) + NMLinkType link_type, + const char *veth_peer) { struct nlattr *info; const char *kind; nm_assert (msg); + nm_assert (!!veth_peer == (link_type == NM_LINK_TYPE_VETH)); kind = nm_link_type_to_rtnl_type_string (link_type); if (!kind) @@ -2151,11 +2410,26 @@ _nl_msg_new_link_set_linkinfo (struct nl_msg *msg, NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); + if (veth_peer) { + struct ifinfomsg ifi = { }; + struct nlattr *data, *info_peer; + + if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) + goto nla_put_failure; + if (!(info_peer = nla_nest_start (msg, 1 /*VETH_INFO_PEER*/))) + goto nla_put_failure; + if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + NLA_PUT_STRING (msg, IFLA_IFNAME, veth_peer); + nla_nest_end (msg, info_peer); + nla_nest_end (msg, data); + } + nla_nest_end (msg, info); return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -2363,7 +2637,7 @@ _nl_msg_new_address (int nlmsg_type, && *((in_addr_t *) address) != 0) { in_addr_t broadcast; - broadcast = *((in_addr_t *) address) | ~nm_utils_ip4_prefix_to_netmask (plen); + broadcast = *((in_addr_t *) address) | ~_nm_utils_ip4_prefix_to_netmask (plen); NLA_PUT (msg, IFA_BROADCAST, addr_len, &broadcast); } @@ -2394,87 +2668,109 @@ nla_put_failure: g_return_val_if_reached (NULL); } +static guint32 +ip_route_get_lock_flag (const NMPlatformIPRoute *route) +{ + return (((guint32) route->lock_window) << RTAX_WINDOW) + | (((guint32) route->lock_cwnd) << RTAX_CWND) + | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) + | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) + | (((guint32) route->lock_mtu) << RTAX_MTU); +} + /* 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, - int nlmsg_flags, - int family, - int ifindex, - NMIPConfigSource source, - unsigned char scope, - gconstpointer network, - guint8 plen, - gconstpointer gateway, - guint32 metric, - guint32 mss, - gconstpointer pref_src, - gconstpointer src, - guint8 src_plen, - guint8 tos, - guint32 window, - guint32 cwnd, - guint32 initcwnd, - guint32 initrwnd, - guint32 mtu, - guint32 lock) + guint16 nlmsgflags, + const NMPObject *obj) { struct nl_msg *msg; + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + gboolean is_v4 = klass->addr_family == AF_INET; + const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj)); + const guint32 table = nm_platform_route_table_uncoerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced, TRUE); struct rtmsg rtmsg = { - .rtm_family = family, - .rtm_tos = tos, - .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ - .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source), - .rtm_scope = scope, + .rtm_family = klass->addr_family, + .rtm_tos = is_v4 + ? obj->ip4_route.tos + : 0, + .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, + .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (obj->ip_route.rt_source), + .rtm_scope = is_v4 + ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) + : RT_SCOPE_NOWHERE, .rtm_type = RTN_UNICAST, .rtm_flags = 0, - .rtm_dst_len = plen, - .rtm_src_len = src ? src_plen : 0, + .rtm_dst_len = obj->ip_route.plen, + .rtm_src_len = is_v4 + ? 0 + : NMP_OBJECT_CAST_IP6_ROUTE (obj)->src_plen, }; gsize addr_len; - nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (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)); - nm_assert (network); - msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); if (!msg) g_return_val_if_reached (NULL); if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; - addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); - NLA_PUT (msg, RTA_DST, addr_len, network); + NLA_PUT (msg, RTA_DST, addr_len, + is_v4 + ? (gconstpointer) &obj->ip4_route.network + : (gconstpointer) &obj->ip6_route.network); - if (src) - NLA_PUT (msg, RTA_SRC, addr_len, src); + if (!is_v4) { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->src)) + NLA_PUT (msg, RTA_SRC, addr_len, &obj->ip6_route.src); + } - NLA_PUT_U32 (msg, RTA_PRIORITY, metric); + NLA_PUT_U32 (msg, RTA_PRIORITY, obj->ip_route.metric); - if (pref_src) - NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); + if (table > 0xFF) + NLA_PUT_U32 (msg, RTA_TABLE, table); + + if (is_v4) { + if (NMP_OBJECT_CAST_IP4_ROUTE (obj)->pref_src) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (obj)->pref_src)) + NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); + } - if (mss || window || cwnd || initcwnd || initrwnd || mtu || lock) { + 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) { struct nlattr *metrics; metrics = nla_nest_start (msg, RTA_METRICS); if (!metrics) goto nla_put_failure; - if (mss) - NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); - if (window) - NLA_PUT_U32 (msg, RTAX_WINDOW, window); - if (cwnd) - NLA_PUT_U32 (msg, RTAX_CWND, cwnd); - if (initcwnd) - NLA_PUT_U32 (msg, RTAX_INITCWND, initcwnd); - if (initrwnd) - NLA_PUT_U32 (msg, RTAX_INITRWND, initrwnd); - if (mtu) - NLA_PUT_U32 (msg, RTAX_MTU, mtu); + if (obj->ip_route.mss) + NLA_PUT_U32 (msg, RTAX_ADVMSS, obj->ip_route.mss); + if (obj->ip_route.window) + NLA_PUT_U32 (msg, RTAX_WINDOW, obj->ip_route.window); + if (obj->ip_route.cwnd) + NLA_PUT_U32 (msg, RTAX_CWND, obj->ip_route.cwnd); + if (obj->ip_route.initcwnd) + NLA_PUT_U32 (msg, RTAX_INITCWND, obj->ip_route.initcwnd); + if (obj->ip_route.initrwnd) + 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 (lock) NLA_PUT_U32 (msg, RTAX_LOCK, lock); @@ -2482,10 +2778,17 @@ _nl_msg_new_route (int nlmsg_type, } /* We currently don't have need for multi-hop routes... */ - if ( gateway - && memcmp (gateway, &nm_ip_addr_zero, addr_len) != 0) - NLA_PUT (msg, RTA_GATEWAY, addr_len, gateway); - NLA_PUT_U32 (msg, RTA_OIF, ifindex); + if (is_v4) { + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); + } else { + if (!IN6_IS_ADDR_UNSPECIFIED (&obj->ip6_route.gateway)) + NLA_PUT (msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); + } + NLA_PUT_U32 (msg, RTA_OIF, obj->ip_route.ifindex); + + if ( !is_v4 + && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) + NLA_PUT_U8 (msg, RTA_PREF, obj->ip6_route.rt_pref); return msg; @@ -2494,56 +2797,27 @@ nla_put_failure: g_return_val_if_reached (NULL); } -/*****************************************************************************/ - -static int _support_kernel_extended_ifa_flags = -1; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; - - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; - - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); - _LOG2D ("kernel-support: extended-ifa-flags: %s", _support_kernel_extended_ifa_flags ? "detected" : "not detected"); -} - -static gboolean -_support_kernel_extended_ifa_flags_get (void) -{ - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); - _support_kernel_extended_ifa_flags = 1; - } - return _support_kernel_extended_ifa_flags; -} - /****************************************************************** * NMPlatform types and functions ******************************************************************/ +typedef enum { + 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_ns; WaitForNlResponseResult *out_seq_result; - gint *out_refresh_all_in_progess; + union { + gint *out_refresh_all_in_progess; + NMPObject **out_route_get; + gpointer out_data; + } response; } DelayedActionWaitForNlResponseData; typedef struct { @@ -2553,11 +2827,12 @@ typedef struct { guint32 nlh_seq_last_handled; #endif guint32 nlh_seq_last_seen; - NMPCache *cache; GIOChannel *event_channel; guint event_id; - gboolean sysctl_get_warned; + bool pruning[_DELAYED_ACTION_IDX_REFRESH_ALL_NUM]; + + bool sysctl_get_warned; GHashTable *sysctl_get_prev_values; NMUdevClient *udev_client; @@ -2578,8 +2853,6 @@ typedef struct { gint is_handling; } delayed_action; - GHashTable *prune_candidates; - GHashTable *wifi_data; } NMLinuxPlatformPrivate; @@ -2599,8 +2872,15 @@ G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) NMPlatform * nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support) { + gboolean use_udev = FALSE; + + if ( nmp_netns_is_initial () + && access ("/sys", W_OK) == 0) + use_udev = TRUE; + return g_object_new (NM_TYPE_LINUX_PLATFORM, NM_PLATFORM_LOG_WITH_PTR, log_with_ptr, + NM_PLATFORM_USE_UDEV, use_udev, NM_PLATFORM_NETNS_SUPPORT, netns_support, NULL); } @@ -2611,13 +2891,6 @@ nm_linux_platform_setup (void) nm_platform_setup (nm_linux_platform_new (FALSE, FALSE)); } -static void -ASSERT_NETNS_CURRENT (NMPlatform *platform) -{ - nm_assert (NM_IS_LINUX_PLATFORM (platform)); - nm_assert (NM_IN_SET (nm_platform_netns_get (platform), NULL, nmp_netns_get_current ())); -} - /*****************************************************************************/ #define ASSERT_SYSCTL_ARGS(pathid, dirfd, path) \ @@ -2676,7 +2949,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; gssize nwrote; - gsize len; + gssize len; char *actual; gs_free char *actual_free = NULL; int errsv; @@ -2731,6 +3004,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat * about to write. */ len = strlen (value) + 1; + nm_assert (len > 0); if (len > 512) actual = actual_free = g_malloc (len + 1); else @@ -2752,16 +3026,27 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat break; } } - if (nwrote == -1 && errsv != EEXIST) { - _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s", - path, value, errsv, strerror (errsv)); + if (nwrote == -1) { + NMLogLevel level = LOGL_ERR; + + if (errsv == EEXIST) { + level = LOGL_DEBUG; + } else if ( errsv == EINVAL + && nm_utils_sysctl_ip_conf_is_path (AF_INET6, path, NULL, "mtu")) { + /* setting the MTU can fail under regular conditions. Suppress + * logging a warning. */ + level = LOGL_DEBUG; + } + + _NMLOG (level, "sysctl: failed to set '%s' to '%s': (%d) %s", + path, value, errsv, strerror (errsv)); } else if (nwrote < len - 1) { _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts", path, value); } if (nwrote < len - 1) { - if (close (fd) != 0) { + if (nm_close (fd) != 0) { if (errsv != 0) errno = errsv; } else if (errsv != 0) @@ -2770,7 +3055,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat errno = EIO; return FALSE; } - if (close (fd) != 0) { + if (nm_close (fd) != 0) { /* errno is already properly set. */ return FALSE; } @@ -2804,7 +3089,7 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char * if (!priv->sysctl_get_prev_values) { _nm_logging_clear_platform_logging_cache = _nm_logging_clear_platform_logging_cache_impl; sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform); - priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + priv->sysctl_get_prev_values = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); } else prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, pathid); @@ -2872,20 +3157,30 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat /*****************************************************************************/ -static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +static NMPlatformKernelSupportFlags +check_kernel_support (NMPlatform *platform, + NMPlatformKernelSupportFlags request_flags) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + NMPlatformKernelSupportFlags response = 0; - return _support_kernel_extended_ifa_flags_get (); -} + nm_assert (NM_IS_LINUX_PLATFORM (platform)); -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) { + if (_support_kernel_extended_ifa_flags_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS; + } - return _support_user_ipv6ll_get (); + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) { + if (_support_user_ipv6ll_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL; + } + + if (NM_FLAGS_HAS (request_flags, NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF)) { + if (_support_rta_pref_get ()) + response |= NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF; + } + + return response; } static void @@ -2896,86 +3191,6 @@ process_events (NMPlatform *platform) /*****************************************************************************/ -#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ - ({ \ - NMPCacheId _cache_id; \ - \ - ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ - nmp_cache_id_init_object_type (&_cache_id, (obj_type), (visible_only)), \ - NULL)); \ - }) - -/*****************************************************************************/ - -static void -do_emit_signal (NMPlatform *platform, const NMPObject *obj, NMPCacheOpsType cache_op, gboolean was_visible) -{ - gboolean is_visible; - NMPObject obj_clone; - const NMPClass *klass; - - nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED)); - - nm_assert (obj || cache_op == NMP_CACHE_OPS_UNCHANGED); - nm_assert (!obj || cache_op == NMP_CACHE_OPS_REMOVED || obj == nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - nm_assert (!obj || cache_op != NMP_CACHE_OPS_REMOVED || obj != nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj)); - - ASSERT_NETNS_CURRENT (platform); - - switch (cache_op) { - case NMP_CACHE_OPS_ADDED: - if (!nmp_object_is_visible (obj)) - return; - break; - case NMP_CACHE_OPS_UPDATED: - is_visible = nmp_object_is_visible (obj); - if (!was_visible && is_visible) - cache_op = NMP_CACHE_OPS_ADDED; - else if (was_visible && !is_visible) { - /* This is a bit ugly. The object was visible and changed in a way that it became invisible. - * We raise a removed signal, but contrary to a real 'remove', @obj is already changed to be - * different from what it was when the user saw it the last time. - * - * The more correct solution would be to have cache_pre_hook() create a clone of the original - * value before it was changed to become invisible. - * - * But, don't bother. Probably nobody depends on the original values and only cares about the - * id properties (which are still correct). - */ - cache_op = NMP_CACHE_OPS_REMOVED; - } else if (!is_visible) - return; - break; - case NMP_CACHE_OPS_REMOVED: - if (!was_visible) - return; - break; - default: - g_assert (cache_op == NMP_CACHE_OPS_UNCHANGED); - return; - } - - klass = NMP_OBJECT_GET_CLASS (obj); - - _LOGt ("emit signal %s %s: %s", - klass->signal_type, - nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - /* don't expose @obj directly, but clone the public fields. A signal handler might - * call back into NMPlatform which could invalidate (or modify) @obj. */ - memcpy (&obj_clone.object, &obj->object, klass->sizeof_public); - g_signal_emit (platform, - _nm_platform_signal_id_get (klass->signal_type_id), - 0, - (int) klass->obj_type, - obj_clone.object.ifindex, - &obj_clone.object, - (int) cache_op); -} - -/*****************************************************************************/ - _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObjectType, DelayedActionType, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (DELAYED_ACTION_TYPE_NONE), NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_LINK, DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS), @@ -3043,11 +3258,12 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns (); char b[255]; - nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT"%s%s)", + nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT", response-type %d%s%s)", data->seq_number, timeout < 0 ? "-" : "", (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND, (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, + (int) data->response_type, data->seq_result ? ", " : "", data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); } else @@ -3109,9 +3325,22 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; if (data->out_seq_result) *data->out_seq_result = seq_result; - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; + switch (data->response_type) { + case DELAYED_ACTION_RESPONSE_TYPE_VOID: + break; + case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: + if (data->response.out_refresh_all_in_progess) { + nm_assert (*data->response.out_refresh_all_in_progess > 0); + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + } + break; + case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: + if (data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + data->response.out_route_get = NULL; + } + break; } g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); @@ -3146,13 +3375,15 @@ delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform, static void delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_master_connected (priv->cache, master_ifindex, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + cache_op = nmp_cache_update_link_master_connected (nm_platform_get_cache (platform), master_ifindex, &obj_old, &obj_new); + if (cache_op == NMP_CACHE_OPS_UNCHANGED) + return; + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } static void @@ -3273,7 +3504,7 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink) any = TRUE; priv->delayed_action.is_handling--; - cache_prune_candidates_prune (platform); + cache_prune_all (platform); return any; } @@ -3319,13 +3550,15 @@ static void delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { DelayedActionWaitForNlResponseData data = { .seq_number = seq_number, .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), .out_seq_result = out_seq_result, - .out_refresh_all_in_progess = out_refresh_all_in_progess, + .response_type = response_type, + .response.out_data = response_out_data, }; delayed_action_schedule (platform, @@ -3336,146 +3569,111 @@ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, /*****************************************************************************/ static void -cache_prune_candidates_record_all (NMPlatform *platform, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - priv->prune_candidates = nmp_cache_lookup_all_to_hash (priv->cache, - nmp_cache_id_init_object_type (&cache_id, obj_type, FALSE), - priv->prune_candidates); - _LOGt ("cache-prune: record %s (now %u candidates)", nmp_class_from_type (obj_type)->obj_type_name, - priv->prune_candidates ? g_hash_table_size (priv->prune_candidates) : 0); -} - -static void -cache_prune_candidates_record_one (NMPlatform *platform, NMPObject *obj) -{ - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - - if (!priv->prune_candidates) - priv->prune_candidates = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) nmp_object_unref, NULL); - - if (_LOGt_ENABLED () && !g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: record-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_add (priv->prune_candidates, nmp_object_ref (obj)); -} - -static void -cache_prune_candidates_drop (NMPlatform *platform, const NMPObject *obj) +cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) { - NMLinuxPlatformPrivate *priv; - - if (!obj) - return; - - priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - if (priv->prune_candidates) { - if (_LOGt_ENABLED () && g_hash_table_contains (priv->prune_candidates, obj)) - _LOGt ("cache-prune: drop-one: %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - g_hash_table_remove (priv->prune_candidates, obj); + NMDedupMultiIter iter; + const NMPObject *obj; + NMPCacheOpsType cache_op; + NMPLookup lookup; + NMPCache *cache = nm_platform_get_cache (platform); + + nmp_lookup_init_obj_type (&lookup, + obj_type); + nm_dedup_multi_iter_init (&iter, + nmp_cache_lookup (cache, + &lookup)); + while (nm_dedup_multi_iter_next (&iter)) { + if (iter.current->dirty) { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + + obj = iter.current->obj; + _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_old, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); + } } } static void -cache_prune_candidates_prune (NMPlatform *platform) +cache_prune_all (NMPlatform *platform) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - GHashTable *prune_candidates; - GHashTableIter iter; - const NMPObject *obj; - gboolean was_visible; - NMPCacheOpsType cache_op; - - if (!priv->prune_candidates) - return; - - prune_candidates = priv->prune_candidates; - priv->prune_candidates = NULL; + DelayedActionType iflags, action_type; - g_hash_table_iter_init (&iter, prune_candidates); - while (g_hash_table_iter_next (&iter, (gpointer *)&obj, NULL)) { - nm_auto_nmpobj NMPObject *obj_cache = NULL; + action_type = DELAYED_ACTION_TYPE_REFRESH_ALL; + FOR_EACH_DELAYED_ACTION (iflags, action_type) { + bool *p = &priv->pruning[delayed_action_refresh_all_to_idx (iflags)]; - _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - cache_op = nmp_cache_remove (priv->cache, obj, TRUE, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + if (*p) { + *p = FALSE; + cache_prune_one_type (platform, delayed_action_refresh_to_object_type (iflags)); + } } - - g_hash_table_unref (prune_candidates); } static void -cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data) +cache_on_change (NMPlatform *platform, + NMPCacheOpsType cache_op, + const NMPObject *obj_old, + const NMPObject *obj_new) { - NMPlatform *platform = user_data; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPClass *klass; char str_buf[sizeof (_nm_utils_to_string_buffer)]; char str_buf2[sizeof (_nm_utils_to_string_buffer)]; + NMPCache *cache = nm_platform_get_cache (platform); - nm_assert (old || new); - nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); - nm_assert (ops_type != NMP_CACHE_OPS_ADDED || (old == NULL && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (ops_type != NMP_CACHE_OPS_REMOVED || (new == NULL && NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old))); - nm_assert (ops_type != NMP_CACHE_OPS_UPDATED || (NMP_OBJECT_IS_VALID (old) && nmp_object_is_alive (old) && NMP_OBJECT_IS_VALID (new) && nmp_object_is_alive (new))); - nm_assert (new == NULL || old == NULL || nmp_object_id_equal (new, old)); - nm_assert (!old || !new || NMP_OBJECT_GET_CLASS (old) == NMP_OBJECT_GET_CLASS (new)); + ASSERT_nmp_cache_ops (cache, cache_op, obj_old, obj_new); + nm_assert (cache_op != NMP_CACHE_OPS_UNCHANGED); - klass = old ? NMP_OBJECT_GET_CLASS (old) : NMP_OBJECT_GET_CLASS (new); - - nm_assert (klass == (new ? NMP_OBJECT_GET_CLASS (new) : NMP_OBJECT_GET_CLASS (old))); + klass = obj_old ? NMP_OBJECT_GET_CLASS (obj_old) : NMP_OBJECT_GET_CLASS (obj_new); _LOGt ("update-cache-%s: %s: %s%s%s", klass->obj_type_name, - (ops_type == NMP_CACHE_OPS_UPDATED + (cache_op == NMP_CACHE_OPS_UPDATED ? "UPDATE" - : (ops_type == NMP_CACHE_OPS_REMOVED + : (cache_op == NMP_CACHE_OPS_REMOVED ? "REMOVE" - : (ops_type == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), - (ops_type != NMP_CACHE_OPS_ADDED - ? nmp_object_to_string (old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) - : nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), - (ops_type == NMP_CACHE_OPS_UPDATED) ? " -> " : "", - (ops_type == NMP_CACHE_OPS_UPDATED - ? nmp_object_to_string (new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) + : (cache_op == NMP_CACHE_OPS_ADDED) ? "ADD" : "???")), + (cache_op != NMP_CACHE_OPS_ADDED + ? nmp_object_to_string (obj_old, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2)) + : nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf2, sizeof (str_buf2))), + (cache_op == NMP_CACHE_OPS_UPDATED) ? " -> " : "", + (cache_op == NMP_CACHE_OPS_UPDATED + ? nmp_object_to_string (obj_new, NMP_OBJECT_TO_STRING_ALL, str_buf, sizeof (str_buf)) : "")); switch (klass->obj_type) { case NMP_OBJECT_TYPE_LINK: { /* check whether changing a slave link can cause a master link (bridge or bond) to go up/down */ - if ( old - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, old->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (old->link.master)); - if ( new - && (!old || old->link.master != new->link.master) - && nmp_cache_link_connected_needs_toggle_by_ifindex (priv->cache, new->link.master, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.master)); + if ( obj_old + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_old->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_old->link.master)); + if ( obj_new + && (!obj_old || obj_old->link.master != obj_new->link.master) + && nmp_cache_link_connected_needs_toggle_by_ifindex (cache, obj_new->link.master, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.master)); } { /* check whether we are about to change a master link that needs toggling connected state. */ - if ( new /* <-- nonsensical, make coverity happy */ - && nmp_cache_link_connected_needs_toggle (cache, new, new, old)) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (new->link.ifindex)); + if ( obj_new /* <-- nonsensical, make coverity happy */ + && nmp_cache_link_connected_needs_toggle (cache, obj_new, obj_new, obj_old)) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_MASTER_CONNECTED, GINT_TO_POINTER (obj_new->link.ifindex)); } { int ifindex = 0; /* if we remove a link (from netlink), we must refresh the addresses and routes */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && !new->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink != old->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && !obj_new->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink != obj_old->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { delayed_action_schedule (platform, @@ -3494,40 +3692,40 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP * Currently, kernel misses to sent us a notification in this case * (https://bugzilla.redhat.com/show_bug.cgi?id=1262908). */ - if ( ops_type == NMP_CACHE_OPS_REMOVED - && old /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink) - ifindex = old->link.ifindex; - else if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && !new->_link.netlink.is_in_netlink) - ifindex = new->link.ifindex; + if ( cache_op == NMP_CACHE_OPS_REMOVED + && obj_old /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink) + ifindex = obj_old->link.ifindex; + else if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && !obj_new->_link.netlink.is_in_netlink) + ifindex = obj_new->link.ifindex; if (ifindex > 0) { - const NMPlatformLink *const *links; - - links = cache_lookup_all_objects (NMPlatformLink, platform, NMP_OBJECT_TYPE_LINK, FALSE); - if (links) { - for (; *links; links++) { - const NMPlatformLink *l = (*links); - - if (l->parent == ifindex) - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); - } + NMPLookup lookup; + NMDedupMultiIter iter; + const NMPlatformLink *l; + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK); + nmp_cache_iter_for_each_link (&iter, + nmp_cache_lookup (cache, &lookup), + &l) { + if (l->parent == ifindex) + delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (l->ifindex)); } } } { /* if a link goes down, we must refresh routes */ - if ( ops_type == NMP_CACHE_OPS_UPDATED - && old && new /* <-- nonsensical, make coverity happy */ - && old->_link.netlink.is_in_netlink - && new->_link.netlink.is_in_netlink - && ( ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_UP)) - || ( NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) - && !NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP)))) { + if ( cache_op == NMP_CACHE_OPS_UPDATED + && obj_old && obj_new /* <-- nonsensical, make coverity happy */ + && obj_old->_link.netlink.is_in_netlink + && obj_new->_link.netlink.is_in_netlink + && ( ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_UP)) + || ( NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) + && !NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP)))) { /* FIXME: I suspect that IFF_LOWER_UP must not be considered, and I * think kernel does send RTM_DELROUTE events for IPv6 routes, so * we might not need to refresh IPv6 routes. */ @@ -3537,17 +3735,17 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP NULL); } } - if ( NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) - && (new && new->_link.netlink.is_in_netlink) - && (!old || !old->_link.netlink.is_in_netlink)) + if ( NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED) + && (obj_new && obj_new->_link.netlink.is_in_netlink) + && (!obj_old || !obj_old->_link.netlink.is_in_netlink)) { - if (!new->_link.netlink.lnk) { + if (!obj_new->_link.netlink.lnk) { /* certain link-types also come with a IFLA_INFO_DATA/lnk_data. It may happen that * kernel didn't send this notification, thus when we first learn about a link * that lacks an lnk_data we re-request it again. * * For example https://bugzilla.redhat.com/show_bug.cgi?id=1284001 */ - switch (new->link.type) { + switch (obj_new->link.type) { case NM_LINK_TYPE_GRE: case NM_LINK_TYPE_IP6TNL: case NM_LINK_TYPE_INFINIBAND: @@ -3558,23 +3756,23 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP case NM_LINK_TYPE_VXLAN: delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); break; default: break; } } - if ( new->link.type == NM_LINK_TYPE_VETH - && new->link.parent == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_VETH + && obj_new->link.parent == 0) { /* the initial notification when adding a veth pair can lack the parent/IFLA_LINK * (https://bugzilla.redhat.com/show_bug.cgi?id=1285827). * Request it again. */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } - if ( new->link.type == NM_LINK_TYPE_ETHERNET - && new->link.addr.len == 0) { + if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET + && obj_new->link.addr.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -3584,7 +3782,7 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP */ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, - GINT_TO_POINTER (new->link.ifindex)); + GINT_TO_POINTER (obj_new->link.ifindex)); } } { @@ -3592,14 +3790,14 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP int ifindex1 = 0, ifindex2 = 0; gboolean changed_master, changed_connected; - changed_master = (new && new->_link.netlink.is_in_netlink && new->link.master > 0 ? new->link.master : 0) - != (old && old->_link.netlink.is_in_netlink && old->link.master > 0 ? old->link.master : 0); - changed_connected = (new && new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (new->link.n_ifi_flags, IFF_LOWER_UP) : 2) - != (old && old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (old->link.n_ifi_flags, IFF_LOWER_UP) : 2); + changed_master = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0 ? obj_new->link.master : 0) + != (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0 ? obj_old->link.master : 0); + changed_connected = (obj_new && obj_new->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_new->link.n_ifi_flags, IFF_LOWER_UP) : 2) + != (obj_old && obj_old->_link.netlink.is_in_netlink ? NM_FLAGS_HAS (obj_old->link.n_ifi_flags, IFF_LOWER_UP) : 2); if (changed_master || changed_connected) { - ifindex1 = (old && old->_link.netlink.is_in_netlink && old->link.master > 0) ? old->link.master : 0; - ifindex2 = (new && new->_link.netlink.is_in_netlink && new->link.master > 0) ? new->link.master : 0; + ifindex1 = (obj_old && obj_old->_link.netlink.is_in_netlink && obj_old->link.master > 0) ? obj_old->link.master : 0; + ifindex2 = (obj_new && obj_new->_link.netlink.is_in_netlink && obj_new->link.master > 0) ? obj_new->link.master : 0; if (ifindex1 > 0) delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex1)); @@ -3607,29 +3805,13 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex2)); } } - { - if ( ( (ops_type == NMP_CACHE_OPS_REMOVED) - || ( (ops_type == NMP_CACHE_OPS_UPDATED) - && new - && !new->_link.netlink.is_in_netlink)) - && old - && old->_link.netlink.is_in_netlink - && old->link.master) { - /* sometimes we receive a wrong RTM_DELLINK message when unslaving - * a device. Refetch the link again to check whether the device - * is really gone. - * - * https://bugzilla.redhat.com/show_bug.cgi?id=1285719#c2 */ - delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (old->link.ifindex)); - } - } break; case NMP_OBJECT_TYPE_IP4_ADDRESS: case NMP_OBJECT_TYPE_IP6_ADDRESS: { /* Address deletion is sometimes accompanied by route deletion. We need to * check all routes belonging to the same interface. */ - if (ops_type == NMP_CACHE_OPS_REMOVED) { + 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 @@ -3643,69 +3825,113 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } } -static void -cache_post (NMPlatform *platform, - struct nlmsghdr *msghdr, - NMPCacheOpsType cache_op, - NMPObject *obj, - NMPObject *obj_cache) +/*****************************************************************************/ + +static guint32 +_nlh_seq_next_get (NMLinuxPlatformPrivate *priv) +{ + /* generate a new sequence number, but skip zero. */ + return priv->nlh_seq_next++ ?: priv->nlh_seq_next++; +} + +/** + * _nl_send_nlmsghdr: + * @platform: + * @nlhdr: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + */ +static int +_nl_send_nlmsghdr (NMPlatform *platform, + struct nlmsghdr *nlhdr, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + guint32 seq; + int nle; - nm_assert (NMP_OBJECT_IS_VALID (obj)); - nm_assert (!obj_cache || nmp_object_id_equal (obj, obj_cache)); - - if (msghdr->nlmsg_type == RTM_NEWROUTE) { - DelayedActionType action_type; - - action_type = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; - if ( !delayed_action_refresh_all_in_progress (platform, action_type) - && nmp_cache_find_other_route_for_same_destination (priv->cache, obj)) { - /* via `iproute route change` the user can update an existing route which effectively - * means that a new object (with a different ID) comes into existance, replacing the - * old on. In other words, as the ID of the object changes, we really see a new - * object with the old one deleted. - * However, kernel decides not to send a RTM_DELROUTE event for that. - * - * To hack around that, check if the update leaves us with multiple routes for the - * same network/plen,metric part. In that case, we cannot do better then requesting - * all routes anew, which sucks. - * - * One mitigation to avoid a dump is only to request a new dump, if we are not in - * the middle of an ongoing dump (delayed_action_refresh_all_in_progress). */ - delayed_action_schedule (platform, action_type, NULL); + nm_assert (nlhdr); + + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; + + { + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + struct iovec iov = { + .iov_base = nlhdr, + .iov_len = nlhdr->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int try_count; + + if (!nlhdr->nlmsg_pid) + nlhdr->nlmsg_pid = nl_socket_get_local_port (priv->nlh); + nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + try_count = 0; +again: + nle = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0); + if (nle < 0) { + nle = errno; + if (nle == EINTR && try_count++ < 100) + goto again; + _LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", g_strerror (nle), nle); + return -nle; } } -} -/*****************************************************************************/ + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + 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_auto_with_seq (NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) +_nl_send_nlmsg (NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nlmsghdr *nlhdr; guint32 seq; int nle; - /* complete the message with a sequence number (ensuring it's not zero). */ - seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++; - - nlmsg_hdr (nlmsg)->nlmsg_seq = seq; + nlhdr = nlmsg_hdr (nlmsg); + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; nle = nl_send_auto (priv->nlh, nlmsg); + if (nle < 0) { + _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nl_geterror (nle), nle); + return nle; + } - if (nle >= 0) { - nle = 0; - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_refresh_all_in_progess); - } else - _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle); - - return nle; + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; } static void @@ -3713,17 +3939,23 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int nle; if (name && !name[0]) name = NULL; g_return_if_fail (ifindex > 0 || name); - _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); + _LOGD ("do-request-link: %d %s", ifindex, name ?: ""); if (ifindex > 0) { - cache_prune_candidates_record_one (platform, - (NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex)); + const NMDedupMultiEntry *entry; + + entry = nmp_cache_lookup_entry_link (nm_platform_get_cache (platform), ifindex); + if (entry) { + priv->pruning[DELAYED_ACTION_IDX_REFRESH_ALL_LINKS] = TRUE; + nm_dedup_multi_entry_set_dirty (entry, TRUE); + } } event_handler_read_netlink (platform, FALSE); @@ -3734,8 +3966,15 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha name, 0, 0); - if (nlmsg) - _nl_send_auto_with_seq (platform, nlmsg, NULL, NULL); + if (nlmsg) { + nle = _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); + if (nle < 0) { + _LOGE ("do-request-link: %d %s: failed sending netlink request \"%s\" (%d)", + ifindex, name ?: "", + nl_geterror (nle), -nle); + return; + } + } } static void @@ -3755,7 +3994,9 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio action_type &= DELAYED_ACTION_TYPE_REFRESH_ALL; FOR_EACH_DELAYED_ACTION (iflags, action_type) { - cache_prune_candidates_record_all (platform, delayed_action_refresh_to_object_type (iflags)); + priv->pruning[delayed_action_refresh_all_to_idx (iflags)] = TRUE; + nmp_cache_dirty_set_all (nm_platform_get_cache (platform), + delayed_action_refresh_to_object_type (iflags)); } FOR_EACH_DELAYED_ACTION (iflags, action_type) { @@ -3795,7 +4036,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_auto_with_seq (platform, nlmsg, NULL, out_refresh_all_in_progess) < 0) { + if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { nm_assert (*out_refresh_all_in_progess > 0); *out_refresh_all_in_progess -= 1; } @@ -3825,13 +4066,12 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) 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); - if (data->seq_number == priv->nlh_seq_last_seen) { - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; - data->out_refresh_all_in_progess = NULL; - break; - } + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progess + && data->seq_number == priv->nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + break; } } } @@ -3879,18 +4119,19 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu static void event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; - nm_auto_nmpobj NMPObject *obj_cache = NULL; NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; - char buf_nlmsg_type[16]; + char buf_nlmsghdr[400]; gboolean id_only = FALSE; - gboolean was_visible; + NMPCache *cache = nm_platform_get_cache (platform); + gboolean is_dump; msghdr = nlmsg_hdr (msg); - if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) + if ( _support_kernel_extended_ifa_flags_still_undecided () + && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); if (!handle_events) @@ -3902,156 +4143,144 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event id_only = TRUE; } - obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + obj = nmp_object_new_from_nl (platform, cache, msg, id_only); if (!obj) { - _LOGT ("event-notification: %s, seq %u: ignore", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq); + _LOGT ("event-notification: %s: ignore", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); return; } - _LOGT ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, - id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - switch (msghdr->nlmsg_type) { - - case RTM_NEWLINK: case RTM_NEWADDR: + case RTM_NEWLINK: case RTM_NEWROUTE: - case RTM_GETLINK: - cache_op = nmp_cache_update_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - - cache_post (platform, msghdr, cache_op, obj, obj_cache); - - do_emit_signal (platform, obj_cache, cache_op, was_visible); - break; - - case RTM_DELLINK: - case RTM_DELADDR: - case RTM_DELROUTE: - cache_op = nmp_cache_remove_netlink (priv->cache, obj, &obj_cache, &was_visible, cache_pre_hook, platform); - do_emit_signal (platform, obj_cache, cache_op, was_visible); + is_dump = delayed_action_refresh_all_in_progress (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj))); break; - default: - break; + is_dump = FALSE; } - cache_prune_candidates_drop (platform, obj_cache); -} - -/*****************************************************************************/ - -static const NMPObject * -cache_lookup_link (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if (!nmp_object_is_visible (obj_cache)) - return NULL; - - return obj_cache; -} - -const NMPlatformObject *const* -nm_linux_platform_lookup (NMPlatform *platform, const NMPCacheId *cache_id, guint *out_len) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), NULL); - g_return_val_if_fail (cache_id, NULL); + _LOGT ("event-notification: %s%s: %s", + _nl_nlmsghdr_to_str (msghdr, buf_nlmsghdr, sizeof (buf_nlmsghdr)), + is_dump ? ", in-dump" : "", + nmp_object_to_string (obj, + id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, + NULL, 0)); - return nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - cache_id, out_len); -} + { + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; + + switch (msghdr->nlmsg_type) { + + case RTM_NEWLINK: + case RTM_NEWADDR: + case RTM_GETLINK: + cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; -static GArray * -link_get_all (NMPlatform *platform) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; + case RTM_NEWROUTE: { + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + gboolean resync_required = FALSE; + gboolean only_dirty = FALSE; + + if (obj->ip_route.rt_cloned) { + /* a cloned route might be a response for RTM_GETROUTE. Check, whether it is. */ + nm_assert (!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)) { + 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++) { + DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, 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) { + *data->response.out_route_get = nmp_object_clone (obj, FALSE); + data->response.out_route_get = NULL; + break; + } + } + } + } + } - return nmp_cache_lookup_multi_to_array (priv->cache, - NMP_OBJECT_TYPE_LINK, - nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, TRUE)); -} + cache_op = nmp_cache_update_netlink_route (cache, + obj, + is_dump, + msghdr->nlmsg_flags, + &obj_old, + &obj_new, + &obj_replace, + &resync_required); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + /* we found an object that is to be replaced by the RTM_NEWROUTE message. + * While we invoke the signal, the platform cache might change and invalidate + * the findings. Mitigate that (for the most part), by marking the entry as + * dirty and only delete @obj_replace if it is still dirty afterwards. + * + * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, + * the signal handlers do not cause to call the platform again, so the cache + * is not really changing. -- if they would, it would anyway be dangerous to overflow + * the stack and it's not ensured that the processing of netlink messages is + * reentrant (maybe it is). + */ + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; + } + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } -static const NMPlatformLink * -_nm_platform_link_get (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj; + if (obj_replace) { + /* the RTM_NEWROUTE message indicates that another route was replaced. + * Remove it now. */ + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_replace, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } + } - obj = cache_lookup_link (platform, ifindex); - return obj ? &obj->link : NULL; -} + if (resync_required) { + /* we'd like to avoid such resyncs as they are expensive and we should only rely on the + * netlink events. This needs investigation. */ + _LOGT ("schedule resync of routes after RTM_NEWROUTE"); + delayed_action_schedule (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + NULL); + } + break; + } -static const NMPlatformLink * -_nm_platform_link_get_by_ifname (NMPlatform *platform, - const char *ifname) -{ - const NMPObject *obj = NULL; + case RTM_DELLINK: + case RTM_DELADDR: + case RTM_DELROUTE: + cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + break; - if (ifname && *ifname) { - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL); + default: + break; + } } - return obj ? &obj->link : NULL; -} - -struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; -}; - -static gboolean -_nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) -{ - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); -} - -static const NMPlatformLink * -_nm_platform_link_get_by_address (NMPlatform *platform, - gconstpointer address, - size_t length) -{ - const NMPObject *obj; - struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, - }; - - if (length <= 0 || length > NM_UTILS_HWADDR_LEN_MAX) - return NULL; - if (!address) - return NULL; - - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, NULL, TRUE, NM_LINK_TYPE_NONE, - (NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d); - return obj ? &obj->link : NULL; -} - -/*****************************************************************************/ - -static const NMPObject * -link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - NM_SET_OUT (out_link, &obj->link); - - if (!obj->_link.netlink.lnk) - return NULL; - if ( link_type != NM_LINK_TYPE_NONE - && ( link_type != obj->link.type - || link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type)) - return NULL; - - return obj->_link.netlink.lnk; } /*****************************************************************************/ @@ -4063,34 +4292,21 @@ do_add_link_with_lookup (NMPlatform *platform, struct nl_msg *nlmsg, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj = NULL; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; + NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { - /* hm, a link with such a name already exists. Try reloading first. */ - do_request_link (platform, 0, name); - - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); - if (obj) { - _LOGE ("do-add-link[%s/%s]: link already exists: %s", - name, - nm_link_type_to_string (link_type), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - return FALSE; - } - } - - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, nm_link_type_to_string (link_type), nl_geterror (nle), -nle); + NM_SET_OUT (out_link, NULL); return FALSE; } @@ -4100,35 +4316,29 @@ do_add_link_with_lookup (NMPlatform *platform, _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-link[%s/%s]: %s", name, nm_link_type_to_string (link_type), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); - - if (!obj) { - /* either kernel signaled failure, or it signaled success and the link object - * is not (yet) in the cache. Try to reload it... */ - do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL); + if (out_link) { + obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); + *out_link = NMP_OBJECT_CAST_LINK (obj); } - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; + return seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } -static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +static NMPlatformError +do_add_addrroute (NMPlatform *platform, + const NMPObject *obj_id, + struct nl_msg *nlmsg, + gboolean suppress_netlink_failure) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - const NMPObject *obj; nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, @@ -4136,72 +4346,69 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - return FALSE; + return NM_PLATFORM_ERROR_NETLINK; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + _NMLOG (( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + || ( suppress_netlink_failure + && seq_result < 0)) ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * FIXME: if the object already existed previously, we might not notice a - * missing update. It's not clear how to fix that reliably without refechting - * all the time. */ - obj = nmp_cache_lookup_obj (priv->cache, obj_id); - if (!obj) { - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - obj = nmp_cache_lookup_obj (priv->cache, obj_id); + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); } - /* Adding is only successful, if kernel reported success *and* we have the - * expected object in cache afterwards. */ - return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + return wait_for_nl_response_to_plerr (seq_result); } static gboolean do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - gboolean success = TRUE; + gboolean success; const char *log_detail = ""; event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - goto out; + return FALSE; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); + success = TRUE; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { /* ok */ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) @@ -4216,42 +4423,60 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * else success = FALSE; - _NMLOG (success ? LOGL_DEBUG : LOGL_ERR, + _NMLOG (success ? LOGL_DEBUG : LOGL_WARN, "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); -out: - if (!nmp_cache_lookup_obj (priv->cache, obj_id)) - return TRUE; + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is still there after we receive the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (nmp_cache_lookup_obj (nm_platform_get_cache (platform), obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); + } - /* such an object still exists in the cache. To be sure, refetch it (and - * hope it's gone) */ - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - return !nmp_cache_lookup_obj (priv->cache, obj_id); + return success; } -static WaitForNlResponseResult -do_change_link_request (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) +static NMPlatformError +do_change_link (NMPlatform *platform, + ChangeLinkType change_link_type, + int ifindex, + struct nl_msg *nlmsg, + const ChangeLinkData *data) { nm_auto_pop_netns NMPNetns *netns = NULL; - WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; + WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + char s_buf[256]; + NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; + NMLogLevel log_level = LOGL_DEBUG; + const char *log_result = "failure"; + const char *log_detail = ""; + gs_free char *log_detail_free = NULL; + const NMPObject *obj_cache; - if (!nm_platform_netns_push (platform, &netns)) - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + if (!nm_platform_netns_push (platform, &netns)) { + log_level = LOGL_ERR; + log_detail = ", failure to change network namespace"; + goto out; + } retry: - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { - _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", - ifindex, - nl_geterror (nle), -nle); - return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + log_level = LOGL_ERR; + log_detail_free = g_strdup_printf (", failure sending netlink request: %s (%d)", + nl_geterror (nle), -nle); + log_detail = log_detail_free; + goto out; } /* always refetch the link after changing it. There seems to be issues @@ -4267,18 +4492,6 @@ retry: nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; goto retry; } - return seq_result; -} - -static NMPlatformError -do_change_link_result (NMPlatform *platform, - int ifindex, - WaitForNlResponseResult seq_result) -{ - char s_buf[256]; - NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS; - NMLogLevel log_level = LOGL_DEBUG; - const char *log_result = "failure", *log_detail = ""; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { log_result = "success"; @@ -4287,38 +4500,43 @@ do_change_link_result (NMPlatform *platform, } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) { log_detail = ", firmware not found"; result = NM_PLATFORM_ERROR_NO_FIRMWARE; + } else if ( NM_IN_SET (-((int) seq_result), ERANGE) + && change_link_type == CHANGE_LINK_TYPE_SET_MTU) { + log_detail = ", setting MTU to requested size is not possible"; + result = NM_PLATFORM_ERROR_CANT_SET_MTU; + } else if ( NM_IN_SET (-((int) seq_result), ENFILE) + && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS + && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) + && obj_cache->link.addr.len == data->set_address.length + && memcmp (obj_cache->link.addr.data, data->set_address.address, data->set_address.length) == 0) { + /* workaround ENFILE which may be wrongly returned (bgo #770456). + * If the MAC address is as expected, assume success? */ + log_result = "success"; + log_detail = " (assume success changing address)"; + result = NM_PLATFORM_ERROR_SUCCESS; } else if (NM_IN_SET (-((int) seq_result), ENODEV)) { log_level = LOGL_DEBUG; result = NM_PLATFORM_ERROR_NOT_FOUND; } else { - log_level = LOGL_ERR; + log_level = LOGL_WARN; result = NM_PLATFORM_ERROR_UNSPECIFIED; } + +out: _NMLOG (log_level, "do-change-link[%d]: %s changing link: %s%s", ifindex, log_result, wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); - return result; } -static NMPlatformError -do_change_link (NMPlatform *platform, - int ifindex, - struct nl_msg *nlmsg) -{ - WaitForNlResponseResult seq_result; - - seq_result = do_change_link_request (platform, ifindex, nlmsg); - return do_change_link_result (platform, ifindex, seq_result); -} - static gboolean link_add (NMPlatform *platform, const char *name, NMLinkType type, + const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link) @@ -4337,9 +4555,6 @@ link_add (NMPlatform *platform, (void) nm_utils_modprobe (NULL, TRUE, "bonding", "max_bonds=0", NULL); } - _LOGD ("link: add link '%s' of type '%s' (%d)", - name, nm_link_type_to_string (type), (int) type); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL, 0, @@ -4352,7 +4567,7 @@ link_add (NMPlatform *platform, if (address && address_len) NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - if (!_nl_msg_new_link_set_linkinfo (nlmsg, type)) + if (!_nl_msg_new_link_set_linkinfo (nlmsg, type, veth_peer)) return FALSE; return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); @@ -4364,11 +4579,10 @@ static gboolean link_delete (NMPlatform *platform, int ifindex) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); NMPObject obj_id; const NMPObject *obj; - obj = nmp_cache_lookup_link (priv->cache, ifindex); + obj = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; @@ -4383,56 +4597,11 @@ link_delete (NMPlatform *platform, int ifindex) return do_delete_object (platform, &obj_id, nlmsg); } -static const char * -link_get_type_name (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - - if (!obj) - return NULL; - - if (obj->link.type != NM_LINK_TYPE_UNKNOWN) { - /* We could detect the @link_type. In this case the function returns - * our internel module names, which differs from rtnl_link_get_type(): - * - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib") - * - NM_LINK_TYPE_TAP (gives "tap", instead of "tun"). - * Note that this functions is only used by NMDeviceGeneric to - * set type_description. */ - return nm_link_type_to_string (obj->link.type); - } - /* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */ - return obj->link.kind ?: "unknown"; -} - -static gboolean -link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *link; - struct udev_device *udevice = NULL; - const char *uproperty; - - link = nmp_cache_lookup_link (priv->cache, ifindex); - if (!link) - return FALSE; - - udevice = link->_link.udev.device; - if (!udevice) - return FALSE; - - uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED"); - if (!uproperty) - return FALSE; - - *unmanaged = nm_udev_utils_property_as_boolean (uproperty); - return TRUE; -} - static gboolean link_refresh (NMPlatform *platform, int ifindex) { do_request_link (platform, ifindex, NULL); - return !!cache_lookup_link (platform, ifindex); + return !!nm_platform_link_get_obj (platform, ifindex, TRUE); } static gboolean @@ -4454,7 +4623,7 @@ link_set_netns (NMPlatform *platform, return FALSE; NLA_PUT (nlmsg, IFLA_NET_NS_FD, 4, &netns_fd); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); @@ -4484,7 +4653,7 @@ link_change_flags (NMPlatform *platform, flags_set); if (!nlmsg) return NM_PLATFORM_ERROR_UNSPECIFIED; - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4519,7 +4688,7 @@ link_set_noarp (NMPlatform *platform, int ifindex) static const char * link_get_udi (NMPlatform *platform, int ifindex) { - const NMPObject *obj = cache_lookup_link (platform, ifindex); + const NMPObject *obj = nm_platform_link_get_obj (platform, ifindex, TRUE); if ( !obj || !obj->_link.netlink.is_in_netlink @@ -4528,35 +4697,21 @@ link_get_udi (NMPlatform *platform, int ifindex) return udev_device_get_syspath (obj->_link.udev.device); } -static struct udev_device * -link_get_udev_device (NMPlatform *platform, int ifindex) -{ - const NMPObject *obj_cache; - - /* we don't use cache_lookup_link() because this would return NULL - * if the link is not visible in libnl. For link_get_udev_device() - * we want to return whatever we have, even if the link itself - * appears invisible via other platform functions. */ - - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - return obj_cache ? obj_cache->_link.udev.device : NULL; -} - static NMPlatformError link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + if (!_support_user_ipv6ll_get ()) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); return NM_PLATFORM_ERROR_OPNOTSUPP; } - _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -4567,7 +4722,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable || !_nl_msg_new_link_set_afspec (nlmsg, mode, NULL)) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - return do_change_link (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); } static gboolean @@ -4582,7 +4737,7 @@ link_set_token (NMPlatform *platform, int ifindex, NMUtilsIPv6IfaceId iid) if (!nlmsg || !_nl_msg_new_link_set_afspec (nlmsg, -1, &iid)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -4606,7 +4761,7 @@ link_supports_vlans (NMPlatform *platform, int ifindex) nm_auto_pop_netns NMPNetns *netns = NULL; const NMPObject *obj; - obj = cache_lookup_link (platform, ifindex); + obj = nm_platform_link_get_obj (platform, ifindex, TRUE); /* Only ARPHRD_ETHER links can possibly support VLANs. */ if (!obj || obj->link.arptype != ARPHRD_ETHER) @@ -4647,8 +4802,12 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; gs_free char *mac = NULL; - WaitForNlResponseResult seq_result; - char s_buf[256]; + const ChangeLinkData d = { + .set_address = { + .address = address, + .length = length, + }, + }; if (!address || !length) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); @@ -4668,32 +4827,32 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); - seq_result = do_change_link_request (platform, ifindex, nlmsg); + return do_change_link (platform, CHANGE_LINK_TYPE_SET_ADDRESS, ifindex, nlmsg, &d); +nla_put_failure: + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); +} - if (NM_IN_SET (-((int) seq_result), ENFILE)) { - const NMPObject *obj_cache; +static NMPlatformError +link_set_name (NMPlatform *platform, int ifindex, const char *name) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - /* workaround ENFILE which may be wrongly returned (bgo #770456). - * If the MAC address is as expected, assume success? */ + _LOGD ("link: change %d: name: %s", ifindex, name); - obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex); - if ( obj_cache - && obj_cache->link.addr.len == length - && memcmp (obj_cache->link.addr.data, address, length) == 0) { - _NMLOG (LOGL_DEBUG, - "do-change-link[%d]: %s changing link: %s%s", - ifindex, - "success", - wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), - " (assume success changing address)"); - return NM_PLATFORM_ERROR_SUCCESS; - } - } + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + 0, + 0); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); - return do_change_link_result (platform, ifindex, seq_result); + NLA_PUT (nlmsg, IFLA_IFNAME, strlen (name) + 1, name); + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: - g_return_val_if_reached (NM_PLATFORM_ERROR_UNSPECIFIED); + g_return_val_if_reached (FALSE); } static gboolean @@ -4710,7 +4869,7 @@ link_get_permanent_address (NMPlatform *platform, return nmp_utils_ethtool_get_permanent_address (ifindex, buf, length); } -static gboolean +static NMPlatformError link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; @@ -4728,7 +4887,7 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_SET_MTU, ifindex, nlmsg, NULL); nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5359,7 +5518,6 @@ link_vlan_change (NMPlatform *platform, const NMVlanQosMapping *egress_map, gsize n_egress_map) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPObject *obj_cache; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; const NMPObjectLnkVlan *lnk; @@ -5371,7 +5529,7 @@ link_vlan_change (NMPlatform *platform, char s_ingress[256]; char s_egress[256]; - obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex); if ( !obj_cache || !obj_cache->_link.netlink.is_in_netlink) { _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); @@ -5437,7 +5595,7 @@ link_vlan_change (NMPlatform *platform, new_n_egress_map)) g_return_val_if_reached (FALSE); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; } static int @@ -5449,9 +5607,6 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, struct ifreq ifr = { }; int fd; - _LOGD ("link: add %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT, - tap ? "tap" : "tun", name, owner, group); - fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC); if (fd < 0) return FALSE; @@ -5467,30 +5622,30 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap, ifr.ifr_flags |= NM_IFF_MULTI_QUEUE; if (ioctl (fd, TUNSETIFF, &ifr)) { - close (fd); + nm_close (fd); return FALSE; } if (owner >= 0 && owner < G_MAXINT32) { if (ioctl (fd, TUNSETOWNER, (uid_t) owner)) { - close (fd); + nm_close (fd); return FALSE; } } if (group >= 0 && group < G_MAXINT32) { if (ioctl (fd, TUNSETGROUP, (gid_t) group)) { - close (fd); + nm_close (fd); return FALSE; } } if (ioctl (fd, TUNSETPERSIST, 1)) { - close (fd); + nm_close (fd); return FALSE; } do_request_link (platform, 0, name); - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, NULL, NULL); @@ -5520,7 +5675,7 @@ link_enslave (NMPlatform *platform, int master, int slave) NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL) == NM_PLATFORM_ERROR_SUCCESS; nla_put_failure: g_return_val_if_reached (FALSE); } @@ -5540,7 +5695,6 @@ _infiniband_partition_action (NMPlatform *platform, int p_key, const NMPlatformLink **out_link) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); nm_auto_close int dirfd = -1; char ifname_parent[IFNAMSIZ]; const NMPObject *obj; @@ -5576,7 +5730,7 @@ _infiniband_partition_action (NMPlatform *platform, if (action == INFINIBAND_ACTION_DELETE_CHILD) return TRUE; - obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, name, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL); if (out_link) *out_link = obj ? &obj->link : NULL; @@ -5714,16 +5868,15 @@ wifi_indicate_addressing_running (NMPlatform *platform, int ifindex, gboolean ru static gboolean link_can_assume (NMPlatform *platform, int ifindex) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformObject *const *objs; - guint i, len; - const NMPObject *link; + NMPLookup lookup; + const NMPObject *link, *o; + NMDedupMultiIter iter; + NMPCache *cache = nm_platform_get_cache (platform); if (ifindex <= 0) return FALSE; - link = cache_lookup_link (platform, ifindex); + link = nm_platform_link_get_obj (platform, ifindex, TRUE); if (!link) return FALSE; @@ -5733,21 +5886,21 @@ link_can_assume (NMPlatform *platform, int ifindex) if (link->link.master > 0) return TRUE; - if (nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex), - NULL)) + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ADDRESS, + ifindex); + if (nmp_cache_lookup (cache, &lookup)) return TRUE; - objs = nmp_cache_lookup_multi (priv->cache, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex), - &len); - if (objs) { - for (i = 0; i < len; i++) { - const NMPlatformIP6Address *a = (NMPlatformIP6Address *) objs[i]; - - if (!IN6_IS_ADDR_LINKLOCAL (&a->address)) - return TRUE; - } + nmp_lookup_init_addrroute (&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)) + return TRUE; } return FALSE; } @@ -5822,33 +5975,6 @@ link_get_driver_info (NMPlatform *platform, /*****************************************************************************/ -static GArray * -ipx_address_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); - - return nmp_cache_lookup_multi_to_array (priv->cache, - obj_type, - nmp_cache_id_init_addrroute_visible_by_ifindex (&cache_id, - obj_type, - ifindex)); -} - -static GArray * -ip4_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ADDRESS); -} - -static GArray * -ip6_address_get_all (NMPlatform *platform, int ifindex) -{ - return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); -} - static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -5877,7 +6003,7 @@ ip4_address_add (NMPlatform *platform, label); nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return do_add_addrroute (platform, &obj_id, nlmsg); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5906,8 +6032,8 @@ ip6_address_add (NMPlatform *platform, preferred, NULL); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - return do_add_addrroute (platform, &obj_id, nlmsg); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5956,303 +6082,139 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui if (!nlmsg) g_return_val_if_reached (FALSE); - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); return do_delete_object (platform, &obj_id, nlmsg); } -static const NMPlatformIP4Address * -ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, guint8 plen, in_addr_t peer_address) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_address; - return NULL; -} - -static const NMPlatformIP6Address * -ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, guint8 plen) -{ - NMPObject obj_id; - const NMPObject *obj; - - nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_address; - return NULL; -} - /*****************************************************************************/ -static GArray * -ipx_route_get_all (NMPlatform *platform, int ifindex, NMPObjectType obj_type, NMPlatformGetRouteFlags flags) +static NMPlatformError +ip_route_add (NMPlatform *platform, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPCacheId cache_id; - const NMPlatformIPRoute *const* routes; - GArray *array; - const NMPClass *klass; - gboolean with_rtprot_kernel; - guint i, len; - - nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - if (!NM_FLAGS_ANY (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT)) - flags |= NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT; - - klass = nmp_class_from_type (obj_type); - - nmp_cache_id_init_routes_visible (&cache_id, - obj_type, - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT), - NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT), - ifindex); - - routes = (const NMPlatformIPRoute *const*) nmp_cache_lookup_multi (priv->cache, &cache_id, &len); - - array = g_array_sized_new (FALSE, FALSE, klass->sizeof_public, len); - - with_rtprot_kernel = NM_FLAGS_HAS (flags, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_RTPROT_KERNEL); - for (i = 0; i < len; i++) { - nm_assert (NMP_OBJECT_GET_CLASS (NMP_OBJECT_UP_CAST (routes[i])) == klass); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj; - if ( with_rtprot_kernel - || routes[i]->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - g_array_append_vals (array, routes[i], 1); + switch (addr_family) { + case AF_INET: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + break; + case AF_INET6: + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + break; + default: + nm_assert_not_reached (); } - return array; -} -static GArray * -ip4_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP4_ROUTE, flags); -} + nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); -static GArray * -ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags flags) -{ - return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); -} - -static guint32 -ip_route_get_lock_flag (NMPlatformIPRoute *route) -{ - return (((guint32) route->lock_window) << RTAX_WINDOW) - | (((guint32) route->lock_cwnd) << RTAX_CWND) - | (((guint32) route->lock_initcwnd) << RTAX_INITCWND) - | (((guint32) route->lock_initrwnd) << RTAX_INITRWND) - | (((guint32) route->lock_mtu) << RTAX_MTU); -} - -static gboolean -ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route) -{ - NMPObject obj_id; - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - in_addr_t network; - - network = nm_utils_ip4_address_clear_host_address (route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET, - route->ifindex, - route->rt_source, - route->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - route->pref_src ? &route->pref_src : NULL, - NULL, - 0, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip4_route (&obj_id, route->ifindex, network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + if (!nlmsg) + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + return do_add_addrroute (platform, + &obj, + nlmsg, + NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean -ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route) +ip_route_delete (NMPlatform *platform, + const NMPObject *obj) { - NMPObject obj_id; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - struct in6_addr network; - - nm_utils_ip6_address_clear_host_address (&network, &route->network, route->plen); - - /* FIXME: take the scope from route into account */ - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, - NLM_F_CREATE | NLM_F_REPLACE, - AF_INET6, - route->ifindex, - route->rt_source, - IN6_IS_ADDR_UNSPECIFIED (&route->gateway) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, - &network, - route->plen, - &route->gateway, - route->metric, - route->mss, - !IN6_IS_ADDR_UNSPECIFIED (&route->pref_src) ? &route->pref_src : NULL, - !IN6_IS_ADDR_UNSPECIFIED (&route->src) ? &route->src : NULL, - route->src_plen, - route->tos, - route->window, - route->cwnd, - route->initcwnd, - route->initrwnd, - route->mtu, - ip_route_get_lock_flag ((NMPlatformIPRoute *) route)); - - nmp_object_stackinit_id_ip6_route (&obj_id, route->ifindex, &network, route->plen, route->metric); - return do_add_addrroute (platform, &obj_id, nlmsg); -} -static gboolean -ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); - network = nm_utils_ip4_address_clear_host_address (network, plen); + if (!NMP_OBJECT_IS_STACKINIT (obj)) + obj_keep_alive = nmp_object_ref (obj); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj); + if (!nlmsg) + g_return_val_if_reached (FALSE); + return do_delete_object (platform, obj, nlmsg); +} - if (metric == 0) { - /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. - * If no route with metric 0 exists, it might delete another route to the same destination. - * For nm_platform_ip4_route_delete() we don't want this semantic. - * - * Instead, make sure that we have the most recent state and process all - * delayed actions (including re-reading data from netlink). */ - delayed_action_handle_all (platform, TRUE); - - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { - /* hmm... we are about to delete an IP4 route with metric 0. We must only - * send the delete request if such a route really exists. Above we refreshed - * the platform cache, still no such route exists. - * - * Be extra careful and reload the routes. We must be sure that such a - * route doesn't exists, because when we add an IPv4 address, we immediately - * afterwards try to delete the kernel-added device route with metric 0. - * It might be, that we didn't yet get the notification about that route. - * - * FIXME: once our ip4_address_add() is sure that upon return we have - * the latest state from in the platform cache, we might save this - * additional expensive cache-resync. */ - do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE); +/*****************************************************************************/ - if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) - return TRUE; - } - } +static NMPlatformError +ip_route_get (NMPlatform *platform, + int addr_family, + gconstpointer address, + int oif_ifindex, + NMPObject **out_route) +{ + const gboolean is_v4 = (addr_family == AF_INET); + const int addr_len = is_v4 ? 4 : 16; + int try_count = 0; + WaitForNlResponseResult seq_result; + int nle; + nm_auto_nlmsg NMPObject *route = NULL; - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + nm_assert (NM_IS_LINUX_PLATFORM (platform)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (address); + + do { + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = addr_family, + .r.rtm_tos = 0, + .r.rtm_dst_len = is_v4 ? 32 : 128, + .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, + }; - return do_delete_object (platform, &obj_id, nlmsg); -} + g_clear_pointer (&route, nmp_object_unref); -static gboolean -ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj_id; + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_DST, address, addr_len)) + nm_assert_not_reached (); - metric = nm_utils_ip6_route_metric_normalize (metric); - - nm_utils_ip6_address_clear_host_address (&network, &network, plen); - - nlmsg = _nl_msg_new_route (RTM_DELROUTE, - 0, - AF_INET6, - ifindex, - NM_IP_CONFIG_SOURCE_UNKNOWN, - RT_SCOPE_NOWHERE, - &network, - plen, - NULL, - metric, - 0, - NULL, - NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); - if (!nlmsg) - return FALSE; + if (oif_ifindex > 0) { + gint32 ii = oif_ifindex; - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_OIF, &ii, sizeof (ii))) + nm_assert_not_reached (); + } - return do_delete_object (platform, &obj_id, nlmsg); -} + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); + if (nle < 0) { + _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", + g_strerror (-nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } -static const NMPlatformIP4Route * -ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + delayed_action_handle_all (platform, FALSE); - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_route; - return NULL; -} + /* Retry, if we failed due to a cache resync. That can happen when the netlink + * socket fills up and we lost the response. */ + } while ( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC + && ++try_count < 10); -static const NMPlatformIP6Route * -ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; + if (seq_result < 0) { + /* negative seq_result is an errno from kernel. Map it to negative + * NMPlatformError (which are also errno). */ + return (NMPlatformError) seq_result; + } - metric = nm_utils_ip6_route_metric_normalize (metric); + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + if (route) { + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return NM_PLATFORM_ERROR_SUCCESS; + } + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + } - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_route; - return NULL; + return NM_PLATFORM_ERROR_UNSPECIFIED; } /*****************************************************************************/ @@ -6351,6 +6313,7 @@ continue_reading: gboolean abort_parsing = FALSE; gboolean process_valid_msg = FALSE; guint32 seq_number; + char buf_nlmsghdr[400]; msg = nlmsg_convert (hdr); if (!msg) { @@ -6370,8 +6333,8 @@ continue_reading: goto stop; } - _LOGt ("netlink: recvmsg: new message type %d, seq %u", - hdr->nlmsg_type, hdr->nlmsg_seq); + _LOGt ("netlink: recvmsg: new message %s", + _nl_nlmsghdr_to_str (hdr, buf_nlmsghdr, sizeof (buf_nlmsghdr))); if (creds) nlmsg_set_creds (msg, creds); @@ -6512,7 +6475,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) nle = event_handler_recvmsgs (platform, TRUE); - if (nle < 0) + if (nle < 0) { switch (nle) { case -NLE_AGAIN: goto after_read; @@ -6543,6 +6506,7 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks) default: _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle); break; + } } any = TRUE; } @@ -6615,19 +6579,19 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, struct udev_device *udevice) { - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - nm_auto_nmpobj NMPObject *obj_cache = NULL; - gboolean was_visible; + nm_auto_nmpobj const NMPObject *obj_old = NULL; + nm_auto_nmpobj const NMPObject *obj_new = NULL; NMPCacheOpsType cache_op; - cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform); + cache_op = nmp_cache_update_link_udev (nm_platform_get_cache (platform), ifindex, udevice, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { nm_auto_pop_netns NMPNetns *netns = NULL; + cache_on_change (platform, cache_op, obj_old, obj_new); if (!nm_platform_netns_push (platform, &netns)) return; - do_emit_signal (platform, obj_cache, cache_op, was_visible); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } } @@ -6683,7 +6647,7 @@ udev_device_removed (NMPlatform *platform, if (ifindex <= 0) { const NMPObject *obj; - obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, + obj = nmp_cache_lookup_link_full (nm_platform_get_cache (platform), 0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice); if (obj) ifindex = obj->link.ifindex; @@ -6735,22 +6699,12 @@ static void nm_linux_platform_init (NMLinuxPlatform *self) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (self); - gboolean use_udev; - - use_udev = nmp_netns_is_initial () - && access ("/sys", W_OK) == 0; priv->nlh_seq_next = 1; - priv->cache = nmp_cache_new (use_udev); 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 = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData)); priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit); - - if (use_udev) { - priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, - handle_udev_event, self); - } } static void @@ -6764,6 +6718,11 @@ constructed (GObject *_object) nm_assert (!platform->_netns || platform->_netns == nmp_netns_get_current ()); + if (nm_platform_get_use_udev (platform)) { + priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL }, + handle_udev_event, platform); + } + _LOGD ("create (%s netns, %s, %s udev)", !platform->_netns ? "ignore" : "use", !platform->_netns && nmp_netns_is_initial () @@ -6773,7 +6732,7 @@ constructed (GObject *_object) : nm_sprintf_bufa (100, "in netns[%p]%s", nmp_netns_get_current (), nmp_netns_get_current () == nmp_netns_get_initial () ? "/main" : "")), - nmp_cache_use_udev_get (priv->cache) ? "use" : "no"); + nm_platform_get_use_udev (platform) ? "use" : "no"); priv->nlh = nl_socket_alloc (); g_assert (priv->nlh); @@ -6874,8 +6833,6 @@ dispose (GObject *object) g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0); g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0); - g_clear_pointer (&priv->prune_candidates, g_hash_table_unref); - G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object); } @@ -6884,8 +6841,6 @@ finalize (GObject *object) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (object); - nmp_cache_free (priv->cache); - 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); @@ -6919,16 +6874,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->sysctl_set = sysctl_set; platform_class->sysctl_get = sysctl_get; - platform_class->link_get = _nm_platform_link_get; - platform_class->link_get_by_ifname = _nm_platform_link_get_by_ifname; - platform_class->link_get_by_address = _nm_platform_link_get_by_address; - platform_class->link_get_all = link_get_all; platform_class->link_add = link_add; platform_class->link_delete = link_delete; - platform_class->link_get_type_name = link_get_type_name; - platform_class->link_get_unmanaged = link_get_unmanaged; - - platform_class->link_get_lnk = link_get_lnk; platform_class->link_refresh = link_refresh; @@ -6940,7 +6887,6 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_noarp = link_set_noarp; platform_class->link_get_udi = link_get_udi; - platform_class->link_get_udev_device = link_get_udev_device; platform_class->link_set_user_ipv6ll_enabled = link_set_user_ipv6ll_enabled; platform_class->link_set_token = link_set_token; @@ -6948,6 +6894,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_set_address = link_set_address; platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; + platform_class->link_set_name = link_set_name; platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs; platform_class->link_get_physical_port_id = link_get_physical_port_id; @@ -6995,26 +6942,16 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_ipip_add = link_ipip_add; platform_class->link_sit_add = link_sit_add; - platform_class->ip4_address_get = ip4_address_get; - platform_class->ip6_address_get = ip6_address_get; - platform_class->ip4_address_get_all = ip4_address_get_all; - platform_class->ip6_address_get_all = ip6_address_get_all; platform_class->ip4_address_add = ip4_address_add; platform_class->ip6_address_add = ip6_address_add; platform_class->ip4_address_delete = ip4_address_delete; platform_class->ip6_address_delete = ip6_address_delete; - platform_class->ip4_route_get = ip4_route_get; - platform_class->ip6_route_get = ip6_route_get; - platform_class->ip4_route_get_all = ip4_route_get_all; - platform_class->ip6_route_get_all = ip6_route_get_all; - platform_class->ip4_route_add = ip4_route_add; - platform_class->ip6_route_add = ip6_route_add; - platform_class->ip4_route_delete = ip4_route_delete; - platform_class->ip6_route_delete = ip6_route_delete; - - platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; - platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; + platform_class->ip_route_add = ip_route_add; + platform_class->ip_route_delete = ip_route_delete; + platform_class->ip_route_get = ip_route_get; + + platform_class->check_kernel_support = check_kernel_support; platform_class->process_events = process_events; } |