diff options
| author | Michael Biebl <biebl@debian.org> | 2022-05-04 15:35:24 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2022-05-04 15:35:24 +0200 |
| commit | 9959fdb2e8ddd06f2161798ca0a39c77d67c652d (patch) | |
| tree | 2ce24a336d2b1c5fd5dec3090db312eded6c78ba /src/libnm-platform | |
| parent | 8c623dddbdebe354cb94bfc559a5371a14865317 (diff) | |
New upstream version 1.37.92 upstream/1.37.92
Diffstat (limited to 'src/libnm-platform')
| -rw-r--r-- | src/libnm-platform/nm-linux-platform.c | 97 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform-utils.c | 12 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform-utils.h | 2 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform.c | 955 | ||||
| -rw-r--r-- | src/libnm-platform/nm-platform.h | 144 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-base.h | 2 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-object.c | 106 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-object.h | 33 | ||||
| -rw-r--r-- | src/libnm-platform/nmp-route-manager.c | 25 | ||||
| -rw-r--r-- | src/libnm-platform/tests/test-nm-platform.c | 2 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils-nl80211.c | 153 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils-private.h | 5 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils-wext.c | 6 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils.c | 25 | ||||
| -rw-r--r-- | src/libnm-platform/wifi/nm-wifi-utils.h | 6 |
15 files changed, 1114 insertions, 459 deletions
diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index ce21ebbb..40cb4396 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -1114,20 +1114,22 @@ _linktype_get_type(NMPlatform *platform, return link_type; } - if (arptype == ARPHRD_LOOPBACK) + switch (arptype) { + case ARPHRD_LOOPBACK: return NM_LINK_TYPE_LOOPBACK; - else if (arptype == ARPHRD_INFINIBAND) + case ARPHRD_INFINIBAND: return NM_LINK_TYPE_INFINIBAND; - else if (arptype == ARPHRD_SIT) + case ARPHRD_SIT: return NM_LINK_TYPE_SIT; - else if (arptype == ARPHRD_TUNNEL6) + case ARPHRD_TUNNEL6: return NM_LINK_TYPE_IP6TNL; - else if (arptype == ARPHRD_PPP) + case ARPHRD_PPP: return NM_LINK_TYPE_PPP; - else if (arptype == ARPHRD_IEEE802154) + case ARPHRD_IEEE802154: return NM_LINK_TYPE_WPAN; - else if (arptype == ARPHRD_6LOWPAN) + case ARPHRD_6LOWPAN: return NM_LINK_TYPE_6LOWPAN; + } { NMPUtilsEthtoolDriverInfo driver_info; @@ -3453,7 +3455,8 @@ _new_from_nl_route(struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter *parse RTN_LOCAL, RTN_BLACKHOLE, RTN_UNREACHABLE, - RTN_PROHIBIT)) + RTN_PROHIBIT, + RTN_THROW)) return NULL; if (nlmsg_parse_arr(nlh, sizeof(struct rtmsg), tb, policy) < 0) @@ -6339,6 +6342,7 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) nm_dedup_multi_iter_init(&iter, nmp_cache_lookup(cache, lookup)); while (nm_dedup_multi_iter_next(&iter)) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; const NMDedupMultiEntry *main_entry; /* we only track the dirty flag for the OBJECT-TYPE index. That means, @@ -6350,7 +6354,7 @@ cache_prune_one_type(NMPlatform *platform, const NMPLookup *lookup) obj = main_entry->obj; _LOGt("cache-prune: prune %s", - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_ALL, sbuf, sizeof(sbuf))); { nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -6390,8 +6394,8 @@ cache_on_change(NMPlatform *platform, const NMPObject *obj_new) { const NMPClass *klass; - char str_buf[sizeof(_nm_utils_to_string_buffer)]; - char str_buf2[sizeof(_nm_utils_to_string_buffer)]; + char str_buf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char str_buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPCache *cache = nm_platform_get_cache(platform); ASSERT_nmp_cache_ops(cache, cache_op, obj_old, obj_new); @@ -7023,6 +7027,7 @@ event_seq_check(NMPlatform *platform, static void event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; @@ -7079,8 +7084,8 @@ event_valid_msg(NMPlatform *platform, struct nl_msg *msg, gboolean handle_events is_dump ? ", in-dump" : "", nmp_object_to_string(obj, is_del ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, - NULL, - 0)); + sbuf1, + sizeof(sbuf1))); while (TRUE) { nm_auto_nmpobj const NMPObject *obj_old = NULL; @@ -7306,6 +7311,7 @@ do_add_addrroute(NMPlatform *platform, struct nl_msg *nlmsg, gboolean suppress_netlink_failure) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; int nle; @@ -7328,7 +7334,7 @@ do_add_addrroute(NMPlatform *platform, 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), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), nm_strerror(nle), -nle); return -NME_PL_NETLINK; @@ -7344,7 +7350,7 @@ do_add_addrroute(NMPlatform *platform, : 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), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf))); if (NMP_OBJECT_GET_TYPE(obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { @@ -7365,6 +7371,7 @@ do_add_addrroute(NMPlatform *platform, static gboolean do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; gs_free char *errmsg = NULL; int nle; @@ -7383,7 +7390,7 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n 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), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), nm_strerror(nle), -nle); return FALSE; @@ -7402,6 +7409,8 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) { /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */ log_detail = ", meaning the address was already removed"; + } else if (NM_IN_SET(-((int) seq_result), ENODEV)) { + log_detail = ", meaning the device was already removed"; } else if (NM_IN_SET(-((int) seq_result), EADDRNOTAVAIL) && NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, @@ -7413,7 +7422,7 @@ do_delete_object(NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *n _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), + nmp_object_to_string(obj_id, NMP_OBJECT_TO_STRING_ID, sbuf1, sizeof(sbuf1)), wait_for_nl_response_to_string(seq_result, errmsg, s_buf, sizeof(s_buf)), log_detail); @@ -7534,6 +7543,21 @@ out: } static int +link_change(NMPlatform *platform, NMLinkType type, int ifindex, gconstpointer extra_data) +{ + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_link(RTM_NEWLINK, 0, ifindex, 0); + if (!nlmsg) + return -NME_UNSPEC; + + if (!_nl_msg_new_link_set_linkinfo(nlmsg, type, extra_data)) + return -NME_UNSPEC; + + return do_change_link(platform, CHANGE_LINK_TYPE_UNSPEC, ifindex, nlmsg, NULL); +} + +static int link_add(NMPlatform *platform, NMLinkType type, const char *name, @@ -8451,6 +8475,7 @@ static gboolean wifi_get_capabilities(NMPlatform *platform, int ifindex, _NMDeviceWifiCapabilities *caps) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + if (caps) *caps = nm_wifi_utils_get_caps(wifi_data); return TRUE; @@ -8460,6 +8485,7 @@ static guint32 wifi_get_frequency(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_freq(wifi_data); } @@ -8471,6 +8497,7 @@ wifi_get_station(NMPlatform *platform, guint32 *out_rate) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_station(wifi_data, out_bssid, out_quality, out_rate); } @@ -8478,6 +8505,7 @@ static _NM80211Mode wifi_get_mode(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, _NM_802_11_MODE_UNKNOWN); + return nm_wifi_utils_get_mode(wifi_data); } @@ -8485,6 +8513,7 @@ static void wifi_set_mode(NMPlatform *platform, int ifindex, _NM80211Mode mode) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_mode(wifi_data, mode); } @@ -8492,6 +8521,7 @@ static void wifi_set_powersave(NMPlatform *platform, int ifindex, guint32 powersave) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_set_powersave(wifi_data, powersave); } @@ -8499,6 +8529,7 @@ static guint32 wifi_find_frequency(NMPlatform *platform, int ifindex, const guint32 *freqs) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_find_freq(wifi_data, freqs); } @@ -8506,6 +8537,7 @@ static void wifi_indicate_addressing_running(NMPlatform *platform, int ifindex, gboolean running) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, ); + nm_wifi_utils_indicate_addressing_running(wifi_data, running); } @@ -8513,6 +8545,7 @@ static _NMSettingWirelessWakeOnWLan wifi_get_wake_on_wlan(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_get_wake_on_wlan(wifi_data); } @@ -8520,9 +8553,26 @@ static gboolean wifi_set_wake_on_wlan(NMPlatform *platform, int ifindex, _NMSettingWirelessWakeOnWLan wowl) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_wake_on_wlan(wifi_data, wowl); } +static gboolean +wifi_get_csme_conn_info(NMPlatform *platform, int ifindex, NMPlatformCsmeConnInfo *out_conn_info) +{ + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + + return nm_wifi_utils_get_csme_conn_info(wifi_data, out_conn_info); +} + +static gboolean +wifi_get_device_from_csme(NMPlatform *platform, int ifindex) +{ + WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + + return nm_wifi_utils_get_device_from_csme(wifi_data); +} + /*****************************************************************************/ static gboolean @@ -8565,6 +8615,7 @@ static guint32 mesh_get_channel(NMPlatform *platform, int ifindex) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, 0); + return nm_wifi_utils_get_mesh_channel(wifi_data); } @@ -8572,6 +8623,7 @@ static gboolean mesh_set_channel(NMPlatform *platform, int ifindex, guint32 channel) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_channel(wifi_data, channel); } @@ -8579,6 +8631,7 @@ static gboolean mesh_set_ssid(NMPlatform *platform, int ifindex, const guint8 *ssid, gsize len) { WIFI_GET_WIFI_DATA_NETNS(wifi_data, platform, ifindex, FALSE); + return nm_wifi_utils_set_mesh_ssid(wifi_data, ssid, len); } @@ -9727,13 +9780,10 @@ constructed(GObject *_object) fd); priv->event_source = - nm_g_unix_fd_source_new(fd, + nm_g_unix_fd_add_source(fd, G_IO_IN | G_IO_NVAL | G_IO_PRI | G_IO_ERR | G_IO_HUP, - G_PRIORITY_DEFAULT, event_handler, - platform, - NULL); - g_source_attach(priv->event_source, NULL); + platform); /* complete construction of the GObject instance before populating the cache. */ G_OBJECT_CLASS(nm_linux_platform_parent_class)->constructed(_object); @@ -9889,6 +9939,7 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->sysctl_get = sysctl_get; platform_class->link_add = link_add; + platform_class->link_change = link_change; platform_class->link_delete = link_delete; platform_class->link_refresh = link_refresh; @@ -9938,6 +9989,8 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass) platform_class->wifi_indicate_addressing_running = wifi_indicate_addressing_running; platform_class->wifi_get_wake_on_wlan = wifi_get_wake_on_wlan; platform_class->wifi_set_wake_on_wlan = wifi_set_wake_on_wlan; + platform_class->wifi_get_csme_conn_info = wifi_get_csme_conn_info; + platform_class->wifi_get_device_from_csme = wifi_get_device_from_csme; platform_class->mesh_get_channel = mesh_get_channel; platform_class->mesh_set_channel = mesh_set_channel; diff --git a/src/libnm-platform/nm-platform-utils.c b/src/libnm-platform/nm-platform-utils.c index 9ad030df..bebc53a8 100644 --- a/src/libnm-platform/nm-platform-utils.c +++ b/src/libnm-platform/nm-platform-utils.c @@ -2132,12 +2132,15 @@ guint32 nmp_utils_lifetime_get(guint32 timestamp, guint32 lifetime, guint32 preferred, - gint32 now, + gint32 *cached_now, guint32 *out_preferred) { - guint32 t_lifetime, t_preferred; + guint32 t_lifetime; + guint32 t_preferred; + gint32 now; - nm_assert(now >= 0); + nm_assert(cached_now); + nm_assert(*cached_now >= 0); if (timestamp == 0 && lifetime == 0) { /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy @@ -2150,8 +2153,7 @@ nmp_utils_lifetime_get(guint32 timestamp, return NM_PLATFORM_LIFETIME_PERMANENT; } - if (now <= 0) - now = nm_utils_get_monotonic_timestamp_sec(); + now = nm_utils_get_monotonic_timestamp_sec_cached(cached_now); t_lifetime = nmp_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now); if (!t_lifetime) { diff --git a/src/libnm-platform/nm-platform-utils.h b/src/libnm-platform/nm-platform-utils.h index a9ccebb3..9f17da48 100644 --- a/src/libnm-platform/nm-platform-utils.h +++ b/src/libnm-platform/nm-platform-utils.h @@ -86,7 +86,7 @@ nmp_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 durati guint32 nmp_utils_lifetime_get(guint32 timestamp, guint32 lifetime, guint32 preferred, - gint32 now, + gint32 *cached_now, guint32 *out_preferred); int nmp_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 922f412d..8866a436 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -1304,6 +1304,45 @@ nm_platform_link_add(NMPlatform *self, ->link_add(self, type, name, parent, address, address_len, mtu, extra_data, out_link); } +int +nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data) +{ + char buf[512]; + const char *name = nm_platform_link_get_name(self, ifindex); + + _CHECK_SELF(self, klass, -NME_BUG); + + _LOG2D("link: changing link: " + "%s " /* type */ + "\"%s\"" /* name */ + "%s" /* extra_data */ + "", + nm_link_type_to_string(type), + name, + ({ + char *buf_p = buf; + gsize buf_len = sizeof(buf); + + buf[0] = '\0'; + + switch (type) { + case NM_LINK_TYPE_BRIDGE: + nm_strbuf_append_str(&buf_p, &buf_len, ", "); + nm_platform_lnk_bridge_to_string((const NMPlatformLnkBridge *) extra_data, + buf_p, + buf_len); + break; + default: + nm_assert(!extra_data); + break; + } + + buf; + })); + + return klass->link_change(self, type, ifindex, extra_data); +} + /** * nm_platform_link_delete: * @self: platform instance @@ -1834,11 +1873,14 @@ nm_platform_link_set_sriov_vfs(NMPlatform *self, int ifindex, const NMPlatformVF g_return_val_if_fail(ifindex > 0, FALSE); - _LOG3D("link: setting VFs"); - for (i = 0; vfs[i]; i++) { - const NMPlatformVF *vf = vfs[i]; + if (_LOGD_ENABLED()) { + _LOG3D("link: setting VFs"); + for (i = 0; vfs[i]; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + const NMPlatformVF *vf = vfs[i]; - _LOG3D("link: VF %s", nm_platform_vf_to_string(vf, NULL, 0)); + _LOG3D("link: VF %s", nm_platform_vf_to_string(vf, sbuf, sizeof(sbuf))); + } } return klass->link_set_sriov_vfs(self, ifindex, vfs); @@ -1855,14 +1897,18 @@ nm_platform_link_set_bridge_vlans(NMPlatform *self, g_return_val_if_fail(ifindex > 0, FALSE); - _LOG3D("link: %s bridge VLANs on %s", - vlans ? "setting" : "clearing", - on_master ? "master" : "self"); - if (vlans) { - for (i = 0; vlans[i]; i++) { - const NMPlatformBridgeVlan *vlan = vlans[i]; - - _LOG3D("link: bridge VLAN %s", nm_platform_bridge_vlan_to_string(vlan, NULL, 0)); + if (_LOGD_ENABLED()) { + _LOG3D("link: %s bridge VLANs on %s", + vlans ? "setting" : "clearing", + on_master ? "master" : "self"); + if (vlans) { + for (i = 0; vlans[i]; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + const NMPlatformBridgeVlan *vlan = vlans[i]; + + _LOG3D("link: bridge VLAN %s", + nm_platform_bridge_vlan_to_string(vlan, sbuf, sizeof(sbuf))); + } } } @@ -2092,7 +2138,10 @@ nm_platform_link_release(NMPlatform *self, int master, int ifindex) if (nm_platform_link_get_master(self, ifindex) != master) return FALSE; - _LOG3D("link: releasing from master '%s'", nm_platform_link_get_name(self, master)); + _LOG3D("link: releasing %d from master '%s' (%d)", + ifindex, + nm_platform_link_get_name(self, master), + master); return klass->link_release(self, master, ifindex); } @@ -3058,6 +3107,28 @@ nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, _NMSettingWirel return klass->wifi_set_wake_on_wlan(self, ifindex, wowl); } +gboolean +nm_platform_wifi_get_csme_conn_info(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info) +{ + _CHECK_SELF(self, klass, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + + return klass->wifi_get_csme_conn_info(self, ifindex, out_conn_info); +} + +gboolean +nm_platform_wifi_get_device_from_csme(NMPlatform *self, int ifindex) +{ + _CHECK_SELF(self, klass, FALSE); + + g_return_val_if_fail(ifindex > 0, FALSE); + + return klass->wifi_get_device_from_csme(self, ifindex); +} + guint32 nm_platform_mesh_get_channel(NMPlatform *self, int ifindex) { @@ -3479,6 +3550,7 @@ nm_platform_ip4_address_add(NMPlatform *self, FALSE); if (_LOGD_ENABLED()) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP4Address addr; addr = (NMPlatformIP4Address){ @@ -3497,7 +3569,7 @@ nm_platform_ip4_address_add(NMPlatform *self, g_strlcpy(addr.label, label, sizeof(addr.label)); _LOG3D("address: adding or updating IPv4 address: %s", - nm_platform_ip4_address_to_string(&addr, NULL, 0)); + nm_platform_ip4_address_to_string(&addr, sbuf, sizeof(sbuf))); } return klass->ip4_address_add(self, ifindex, @@ -3529,6 +3601,7 @@ nm_platform_ip6_address_add(NMPlatform *self, g_return_val_if_fail(preferred <= lifetime, FALSE); if (_LOGD_ENABLED()) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformIP6Address addr = {0}; addr.ifindex = ifindex; @@ -3541,7 +3614,7 @@ nm_platform_ip6_address_add(NMPlatform *self, addr.n_ifa_flags = flags; _LOG3D("address: adding or updating IPv6 address: %s", - nm_platform_ip6_address_to_string(&addr, NULL, 0)); + nm_platform_ip6_address_to_string(&addr, sbuf, sizeof(sbuf))); } return klass ->ip6_address_add(self, ifindex, address, plen, peer_address, lifetime, preferred, flags); @@ -3592,6 +3665,41 @@ nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr ad return klass->ip6_address_delete(self, ifindex, address, plen); } +const NMPObject * +nm_platform_ip_address_get(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (NMPlatformIPAddress *) */ needle) +{ + const NMPlatformIPXAddress *addr; + NMPObject obj_id; + const NMPObject *obj; + + nm_assert(NM_IS_PLATFORM(self)); + nm_assert_addr_family(addr_family); + nm_assert(needle); + + addr = needle; + + if (ifindex <= 0) { + /* We allow the caller to override the ifindex. */ + ifindex = addr->ax.ifindex; + } + + if (NM_IS_IPv4(addr_family)) { + nmp_object_stackinit_id_ip4_address(&obj_id, + ifindex, + addr->a4.address, + addr->a4.plen, + addr->a4.peer_address); + } else + nmp_object_stackinit_id_ip6_address(&obj_id, ifindex, &addr->a6.address); + + obj = nmp_cache_lookup_obj(nm_platform_get_cache(self), &obj_id); + nm_assert(!obj || nmp_object_is_visible(obj)); + return obj; +} + const NMPlatformIP4Address * nm_platform_ip4_address_get(NMPlatform *self, int ifindex, @@ -3632,7 +3740,7 @@ static gboolean _addr_array_clean_expired(int addr_family, int ifindex, GPtrArray *array, - guint32 now, + gint32 *cached_now, GHashTable **idx) { guint i; @@ -3640,7 +3748,8 @@ _addr_array_clean_expired(int addr_family, nm_assert_addr_family(addr_family); nm_assert(ifindex > 0); - nm_assert(now > 0); + nm_assert(cached_now); + nm_assert(*cached_now >= 0); if (!array) return FALSE; @@ -3674,17 +3783,16 @@ _addr_array_clean_expired(int addr_family, goto clear_and_next; } - if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, now, NULL)) + if (!nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, cached_now, NULL)) goto clear_and_next; - if (idx) { - if (G_UNLIKELY(!*idx)) { - *idx = g_hash_table_new((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal); - } - if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a))) - nm_assert_not_reached(); + if (G_UNLIKELY(!*idx)) { + *idx = + g_hash_table_new((GHashFunc) nmp_object_id_hash, (GEqualFunc) nmp_object_id_equal); } + if (!g_hash_table_add(*idx, (gpointer) NMP_OBJECT_UP_CAST(a))) + nm_assert_not_reached(); + any_addrs = TRUE; continue; @@ -3698,8 +3806,8 @@ clear_and_next: static gboolean ip4_addr_subnets_is_plain_address(const GPtrArray *addresses, gconstpointer needle) { - return needle >= (gconstpointer) &addresses->pdata[0] - && needle < (gconstpointer) &addresses->pdata[addresses->len]; + return nm_ptr_to_uintptr(needle) >= nm_ptr_to_uintptr(&addresses->pdata[0]) + && nm_ptr_to_uintptr(needle) < nm_ptr_to_uintptr(&addresses->pdata[addresses->len]); } static const NMPObject ** @@ -3733,6 +3841,30 @@ ip4_addr_subnets_destroy_index(GHashTable *subnets, const GPtrArray *addresses) g_hash_table_unref(subnets); } +static guint +_ip4_addr_subnets_hash(gconstpointer ptr) +{ + const NMPlatformIP4Address *addr = NMP_OBJECT_CAST_IP4_ADDRESS(ptr); + NMHashState h; + + nm_hash_init(&h, 3282159733); + nm_hash_update_vals(&h, + addr->plen, + nm_utils_ip4_address_clear_host_address(addr->address, addr->plen)); + return nm_hash_complete(&h); +} + +static gboolean +_ip4_addr_subnets_equal(gconstpointer p_a, gconstpointer p_b) +{ + const NMPlatformIP4Address *a = NMP_OBJECT_CAST_IP4_ADDRESS(p_a); + const NMPlatformIP4Address *b = NMP_OBJECT_CAST_IP4_ADDRESS(p_b); + + return a->plen == b->plen + && (nm_utils_ip4_address_clear_host_address(a->address, a->plen) + == nm_utils_ip4_address_clear_host_address(b->address, b->plen)); +} + static GHashTable * ip4_addr_subnets_build_index(const GPtrArray *addresses, gboolean consider_flags, @@ -3743,34 +3875,35 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, nm_assert(addresses && addresses->len); - subnets = g_hash_table_new(nm_direct_hash, NULL); + subnets = g_hash_table_new(_ip4_addr_subnets_hash, _ip4_addr_subnets_equal); /* Build a hash table of all addresses per subnet */ for (i = 0; i < addresses->len; i++) { + const NMPObject **p_obj; + const NMPObject *obj; const NMPlatformIP4Address *address; - gpointer p_address; GPtrArray *addr_list; - guint32 net; int position; gpointer p; if (!addresses->pdata[i]) continue; - p_address = &addresses->pdata[i]; - address = NMP_OBJECT_CAST_IP4_ADDRESS(addresses->pdata[i]); + p_obj = (const NMPObject **) &addresses->pdata[i]; + obj = *p_obj; - net = address->address & _nm_utils_ip4_prefix_to_netmask(address->plen); - if (!g_hash_table_lookup_extended(subnets, GUINT_TO_POINTER(net), NULL, &p)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + if (!g_hash_table_lookup_extended(subnets, obj, NULL, &p)) { + g_hash_table_insert(subnets, (gpointer) obj, p_obj); continue; } nm_assert(p); + address = NMP_OBJECT_CAST_IP4_ADDRESS(obj); + if (full_index) { if (ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = g_ptr_array_new(); - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), addr_list); + g_hash_table_insert(subnets, (gpointer) obj, addr_list); g_ptr_array_add(addr_list, p); } else addr_list = p; @@ -3779,13 +3912,13 @@ ip4_addr_subnets_build_index(const GPtrArray *addresses, position = -1; /* append */ else position = 0; /* prepend */ - g_ptr_array_insert(addr_list, position, p_address); + g_ptr_array_insert(addr_list, position, p_obj); } else { /* we only care about the primary. No need to track the secondaries * as a GPtrArray. */ nm_assert(ip4_addr_subnets_is_plain_address(addresses, p)); if (consider_flags && !NM_FLAGS_HAS(address->n_ifa_flags, IFA_F_SECONDARY)) { - g_hash_table_insert(subnets, GUINT_TO_POINTER(net), p_address); + g_hash_table_insert(subnets, (gpointer) obj, p_obj); } } } @@ -3811,16 +3944,11 @@ ip4_addr_subnets_is_secondary(const NMPObject *address, const GPtrArray *addresses, const GPtrArray **out_addr_list) { - const NMPlatformIP4Address *a; - const GPtrArray *addr_list; - gconstpointer p; - guint32 net; - const NMPObject **o; + const GPtrArray *addr_list; + gconstpointer p; + const NMPObject **o; - a = NMP_OBJECT_CAST_IP4_ADDRESS(address); - - net = a->address & _nm_utils_ip4_prefix_to_netmask(a->plen); - p = g_hash_table_lookup(subnets, GUINT_TO_POINTER(net)); + p = g_hash_table_lookup(subnets, address); nm_assert(p); if (!ip4_addr_subnets_is_plain_address(addresses, p)) { addr_list = p; @@ -3857,41 +3985,33 @@ ip6_address_scope(const NMPlatformIP6Address *a) } static int -ip6_address_scope_cmp(gconstpointer p_a, gconstpointer p_b, gpointer increasing) +ip6_address_scope_cmp_ascending(gconstpointer p_a, gconstpointer p_b, gpointer unused) { - const NMPlatformIP6Address *a; - const NMPlatformIP6Address *b; - - if (!increasing) - NM_SWAP(&p_a, &p_b); - - a = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a); - b = NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b); - - NM_CMP_DIRECT(ip6_address_scope(a), ip6_address_scope(b)); + NM_CMP_DIRECT(ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_a)), + ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS(*(const NMPObject *const *) p_b))); return 0; } +static int +ip6_address_scope_cmp_descending(gconstpointer p_a, gconstpointer p_b, gpointer unused) +{ + return ip6_address_scope_cmp_ascending(p_b, p_a, NULL); +} + /** * nm_platform_ip_address_sync: * @self: platform instance * @addr_family: the address family AF_INET or AF_INET6. * @ifindex: Interface index - * @known_addresses: List of addresses. The list will be modified and only - * addresses that were successfully added will be kept in the list. - * That means, expired addresses and addresses that could not be added - * will be dropped. - * Hence, the input argument @known_addresses is also an output argument - * telling which addresses were successfully added. - * Addresses are removed by unrefing the instance via nmp_object_unref() - * and leaving a NULL tombstone. + * @known_addresses: List of addresses. The list will be modified and + * expired addresses will be cleared (by calling nmp_object_unref() + * on the array element). * @addresses_prune: (allow-none): the list of addresses to delete. * If platform has such an address configured, it will be deleted * at the beginning of the sync. Note that the array will be modified * by the function. - * Note that the addresses must be properly sorted, by their priority. - * Create this list with nm_platform_ip_address_get_prune_list() which - * gets the sorting right. + * Addresses that are both contained in @known_addresses and @addresses_prune + * will be configured. * * A convenience function to synchronize addresses for a specific interface * with the least possible disturbance. It simply removes addresses that are @@ -3906,11 +4026,14 @@ nm_platform_ip_address_sync(NMPlatform *self, GPtrArray *known_addresses, GPtrArray *addresses_prune) { - const gint32 now = nm_utils_get_monotonic_timestamp_sec(); - const int IS_IPv4 = NM_IS_IPv4(addr_family); - gs_unref_hashtable GHashTable *known_addresses_idx = NULL; - GPtrArray *plat_addresses; - GHashTable *known_subnets = NULL; + gint32 now = 0; + const int IS_IPv4 = NM_IS_IPv4(addr_family); + NMPLookup lookup; + const gboolean EXTRA_LOGGING = FALSE; + gs_unref_hashtable GHashTable *known_addresses_idx = NULL; + gs_unref_hashtable GHashTable *plat_addrs_to_delete = NULL; + gs_unref_ptrarray GPtrArray *plat_addresses = NULL; + gboolean success; guint i_plat; guint i_know; guint i; @@ -3918,74 +4041,181 @@ nm_platform_ip_address_sync(NMPlatform *self, _CHECK_SELF(self, klass, FALSE); +#define _plat_addrs_to_delete_ensure(ptr) \ + ({ \ + GHashTable **_ptr = (ptr); \ + \ + if (!*_ptr) { \ + *_ptr = g_hash_table_new_full((GHashFunc) nmp_object_id_hash, \ + (GEqualFunc) nmp_object_id_equal, \ + (GDestroyNotify) nmp_object_unref, \ + NULL); \ + } \ + *_ptr; \ + }) + + /* Disabled. Enable this for printf debugging. */ + if (EXTRA_LOGGING) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf1[50]; + + _LOG3T("IPv%c address sync on %d (%u addresses, %u to prune)", + nm_utils_addr_family_to_char(addr_family), + ifindex, + nm_g_ptr_array_len(known_addresses), + nm_g_ptr_array_len(addresses_prune)); + for (i = 0; known_addresses && i < known_addresses->len; i++) { + _LOG3T(" address#%u: %s%s", + i, + nmp_object_to_string(known_addresses->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf)), + IS_IPv4 ? "" + : nm_sprintf_buf(sbuf1, + " (scope %d)", + (int) ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS( + known_addresses->pdata[i])))); + } + for (i = 0; addresses_prune && i < addresses_prune->len; i++) { + _LOG3T(" prune #%u: %s", + i, + nmp_object_to_string(addresses_prune->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf))); + } + } + + /* @known_addresses are in decreasing priority order (highest priority addresses first). */ + /* The order we want to enforce is only among addresses with the same * scope, as the kernel keeps addresses sorted by scope. Therefore, * apply the same sorting to known addresses, so that we don't try to * unnecessary change the order of addresses with different scopes. */ if (!IS_IPv4) { if (known_addresses) - g_ptr_array_sort_with_data(known_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(TRUE)); + g_ptr_array_sort_with_data(known_addresses, ip6_address_scope_cmp_descending, NULL); } if (!_addr_array_clean_expired(addr_family, ifindex, known_addresses, - now, + &now, &known_addresses_idx)) known_addresses = NULL; - /* @plat_addresses must be sorted in decreasing priority order (highest priority addresses first), contrary to - * @known_addresses which is in increasing priority order (lowest priority addresses first). */ - plat_addresses = addresses_prune; + if (nm_g_ptr_array_len(addresses_prune) > 0) { + /* First delete addresses that we should prune (and which are no longer tracked + * as @known_addresses. */ + for (i = 0; i < addresses_prune->len; i++) { + const NMPObject *prune_obj = addresses_prune->pdata[i]; + + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(prune_obj), + NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP6_ADDRESS)); + + if (nm_g_hash_table_contains(known_addresses_idx, prune_obj)) + continue; + + nm_platform_ip_address_delete(self, + addr_family, + ifindex, + NMP_OBJECT_CAST_IP_ADDRESS(prune_obj)); + } + } + + /* ensure we have the platform cache up to date. */ + nm_platform_process_events(self); + + /* @plat_addresses for IPv6 must be sorted in decreasing priority order (highest priority addresses first). + * IPv4 are probably unsorted or sorted with lowest priority first, but their order doesn't matter because + * we check the "secondary" flag. */ + plat_addresses = nm_platform_lookup_clone( + self, + nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4), ifindex), + NULL, + NULL); + + if (EXTRA_LOGGING && plat_addresses) { + for (i = 0; i < plat_addresses->len; i++) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf1[50]; + + _LOG3T(" platform#%u: %s%s", + i, + nmp_object_to_string(plat_addresses->pdata[i], + NMP_OBJECT_TO_STRING_ALL, + sbuf, + sizeof(sbuf)), + IS_IPv4 ? "" + : nm_sprintf_buf(sbuf1, + " (scope %d)", + (int) ip6_address_scope(NMP_OBJECT_CAST_IP6_ADDRESS( + plat_addresses->pdata[i])))); + } + } if (nm_g_ptr_array_len(plat_addresses) > 0) { - /* Delete unknown addresses */ + /* Delete addresses that interfere with our intended order. */ if (IS_IPv4) { - GHashTable *plat_subnets; + GHashTable *known_subnets = NULL; + GHashTable *plat_subnets; + gs_free bool *plat_handled_to_free = NULL; + bool *plat_handled = NULL; + + /* For IPv4, we only consider it a conflict for addresses in the same + * subnet. That's where kernel will assign a primary/secondary flag. + * For different subnets, we don't define the order. */ plat_subnets = ip4_addr_subnets_build_index(plat_addresses, TRUE, TRUE); for (i = 0; i < plat_addresses->len; i++) { - const NMPObject *plat_obj; + const NMPObject *plat_obj = plat_addresses->pdata[i]; + const NMPObject *known_obj; const NMPlatformIP4Address *plat_address; const GPtrArray *addr_list; + gboolean secondary; - plat_obj = plat_addresses->pdata[i]; - if (!plat_obj) { - /* Already deleted */ + if (plat_handled && plat_handled[i]) continue; - } - plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); - if (known_addresses) { - const NMPObject *o; + if (!known_obj) { + /* this address is added externally. Even if it's presence would mess + * with our desired order, we cannot delete it. Skip it. */ + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; + continue; + } - o = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (o) { - gboolean secondary; + if (!known_subnets) + known_subnets = ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); - if (!known_subnets) - known_subnets = - ip4_addr_subnets_build_index(known_addresses, FALSE, FALSE); + plat_address = NMP_OBJECT_CAST_IP4_ADDRESS(plat_obj); - secondary = - ip4_addr_subnets_is_secondary(o, known_subnets, known_addresses, NULL); - if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { - /* if we have an existing known-address, with matching secondary role, - * do not delete the platform-address. */ - continue; - } - } + secondary = + ip4_addr_subnets_is_secondary(known_obj, known_subnets, known_addresses, NULL); + if (secondary == NM_FLAGS_HAS(plat_address->n_ifa_flags, IFA_F_SECONDARY)) { + /* if we have an existing known-address, with matching secondary role, + * do not delete the platform-address. */ + continue; } - nm_platform_ip4_address_delete(self, - ifindex, - plat_address->address, - plat_address->plen, - plat_address->peer_address); + if (!plat_handled) { + plat_handled = nm_malloc0_maybe_a(300, + sizeof(bool) * plat_addresses->len, + &plat_handled_to_free); + } + plat_handled[i] = TRUE; + + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(plat_obj)); if (!ip4_addr_subnets_is_secondary(plat_obj, plat_subnets, @@ -3999,74 +4229,73 @@ nm_platform_ip_address_sync(NMPlatform *self, * addresses are deleted, so that we can start with a clean * slate and add addresses in the right order. */ for (j = 1; j < addr_list->len; j++) { - const NMPObject **o; + const NMPObject **o = ip4_addr_subnets_addr_list_get(addr_list, j); + guint o_idx; - o = ip4_addr_subnets_addr_list_get(addr_list, j); - nm_assert(o); + o_idx = (o - ((const NMPObject **) &plat_addresses->pdata[0])); - if (*o) { - const NMPlatformIP4Address *a; + nm_assert(o_idx < plat_addresses->len); + nm_assert(o == ((const NMPObject **) &plat_addresses->pdata[o_idx])); - a = NMP_OBJECT_CAST_IP4_ADDRESS(*o); - nm_platform_ip4_address_delete(self, - ifindex, - a->address, - a->plen, - a->peer_address); - nmp_object_unref(*o); - *o = NULL; + if (plat_handled[o_idx]) + continue; + + plat_handled[o_idx] = TRUE; + + if (!nm_g_hash_table_contains(known_addresses_idx, *o)) { + /* Again, this is an external address. We cannot delete + * it to fix the address order. Pass. */ + continue; } + + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(*o)); } } } ip4_addr_subnets_destroy_index(plat_subnets, plat_addresses); + ip4_addr_subnets_destroy_index(known_subnets, known_addresses); } else { - guint known_addresses_len; IP6AddrScope cur_scope; gboolean delete_remaining_addrs; - g_ptr_array_sort_with_data(plat_addresses, - ip6_address_scope_cmp, - GINT_TO_POINTER(FALSE)); + /* For IPv6, we only compare addresses per-scope. Addresses in different + * scopes don't have a defined order. */ - known_addresses_len = known_addresses ? known_addresses->len : 0; + g_ptr_array_sort_with_data(plat_addresses, ip6_address_scope_cmp_descending, NULL); - /* First, compare every address whether it is still a "known address", that is, whether - * to keep it or to delete it. - * - * If we don't find a matching valid address in @known_addresses, we will delete - * plat_addr. - * - * Certain addresses, like temporary addresses, are ignored by this function - * if not run with full_sync. These addresses are usually not managed by NetworkManager - * directly, or at least, they are not managed via nm_platform_ip6_address_sync(). - * Only in full_sync mode, we really want to get rid of them (usually, when we take - * the interface down). - * - * Note that we mark handled addresses by setting it to %NULL in @plat_addresses array. */ + /* First, check that existing addresses have a matching plen as the ones + * we are about to configure (@known_addresses). If not, delete them. */ for (i_plat = 0; i_plat < plat_addresses->len; i_plat++) { - const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; - const NMPObject *know_obj; - const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); - - if (known_addresses_idx) { - know_obj = g_hash_table_lookup(known_addresses_idx, plat_obj); - if (know_obj - && plat_addr->plen == NMP_OBJECT_CAST_IP6_ADDRESS(know_obj)->plen) { - /* technically, plen is not part of the ID for IPv6 addresses and thus - * @plat_addr is essentially the same address as @know_addr (regrading - * its identity, not its other attributes). - * However, we cannot modify an existing addresses' plen without - * removing and readding it. Thus, only keep plat_addr, if the plen - * matches. - * - * keep this one, and continue */ - continue; - } + const NMPObject *plat_obj = plat_addresses->pdata[i_plat]; + const NMPObject *known_obj; + + known_obj = nm_g_hash_table_lookup(known_addresses_idx, plat_obj); + if (!known_obj) { + /* We don't know this address. It was added externally. Keep it configured. + * We also don't want to delete the address below, so mark it as handled + * by clearing the pointer. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + continue; } - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); - nmp_object_unref(g_steal_pointer(&plat_addresses->pdata[i_plat])); + if (NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)->plen + != NMP_OBJECT_CAST_IP6_ADDRESS(known_obj)->plen) { + /* technically, plen is not part of the ID for IPv6 addresses and thus + * @plat_addr is essentially the same address as @know_addr (w.r.t. + * its identity, not its other attributes). + * However, we cannot modify an existing addresses' plen without + * removing and readding it. Thus, we need to delete plat_addr. + * + * We don't just add this address to @plat_addrs_to_delete, because + * it's too different. Instead, delete and re-add below. */ + nm_platform_ip_address_delete(self, + AF_INET6, + ifindex, + NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj)); + /* Mark address as handled. */ + nm_clear_pointer(&plat_addresses->pdata[i_plat], nmp_object_unref); + } } /* Next, we must preserve the priority of the routes. That is, source address @@ -4077,16 +4306,17 @@ nm_platform_ip_address_sync(NMPlatform *self, * @known_addresses (which has lowest priority first). * * If we find a first discrepancy, we need to delete all remaining addresses - * with same scope from that point on, because below we must re-add all the + * for same scope from that point on, because below we must re-add all the * addresses in the right order to get their priority right. */ cur_scope = IP6_ADDR_SCOPE_LOOPBACK; delete_remaining_addrs = FALSE; i_plat = plat_addresses->len; - i_know = 0; + i_know = nm_g_ptr_array_len(known_addresses); + while (i_plat > 0) { - const NMPlatformIP6Address *plat_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(plat_addresses->pdata[--i_plat]); - IP6AddrScope plat_scope; + const NMPObject *plat_obj = plat_addresses->pdata[--i_plat]; + const NMPlatformIP6Address *plat_addr = NMP_OBJECT_CAST_IP6_ADDRESS(plat_obj); + IP6AddrScope plat_scope; if (!plat_addr) continue; @@ -4099,10 +4329,9 @@ nm_platform_ip_address_sync(NMPlatform *self, } if (!delete_remaining_addrs) { - delete_remaining_addrs = TRUE; - for (; i_know < known_addresses_len; i_know++) { + while (i_know > 0) { const NMPlatformIP6Address *know_addr = - NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[i_know]); + NMP_OBJECT_CAST_IP6_ADDRESS(known_addresses->pdata[--i_know]); IP6AddrScope know_scope; if (!know_addr) @@ -4114,18 +4343,18 @@ nm_platform_ip_address_sync(NMPlatform *self, if (IN6_ARE_ADDR_EQUAL(&plat_addr->address, &know_addr->address)) { /* we have a match. Mark address as handled. */ - i_know++; - delete_remaining_addrs = FALSE; goto next_plat; } - /* plat_address has no match. Now delete_remaining_addrs is TRUE and we will - * delete all the remaining addresses with cur_scope. */ + /* "plat_address" has no match. "delete_remaining_addrs" will be set to TRUE and we will + * delete all the remaining addresses with "cur_scope". */ break; } + delete_remaining_addrs = TRUE; } - nm_platform_ip6_address_delete(self, ifindex, plat_addr->address, plat_addr->plen); + g_hash_table_add(_plat_addrs_to_delete_ensure(&plat_addrs_to_delete), + (gpointer) nmp_object_ref(plat_obj)); next_plat:; } } @@ -4134,33 +4363,69 @@ next_plat:; if (!known_addresses) return TRUE; - if (IS_IPv4) - ip4_addr_subnets_destroy_index(known_subnets, known_addresses); + success = TRUE; /* Add missing addresses. New addresses are added by kernel with top * priority. */ - for (i_know = 0; i_know < known_addresses->len; i_know++) { + for (i = 0; i < known_addresses->len; i++) { + const NMPObject *plat_obj; + const NMPObject *known_obj; const NMPlatformIPXAddress *known_address; - const NMPObject *o; guint32 lifetime; guint32 preferred; - o = known_addresses->pdata[i_know]; - if (!o) + /* IPv4 addresses we need to add in the order most important first. + * IPv6 addresses we need to add in the reverse order with least + * important first. Kernel will interpret the last address as most + * important. + * + * @known_addresses is always in the order most-important-first. */ + i_know = IS_IPv4 ? i : (known_addresses->len - i - 1u); + + known_obj = known_addresses->pdata[i_know]; + if (!known_obj) continue; - nm_assert(NMP_OBJECT_GET_TYPE(o) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); + nm_assert(NMP_OBJECT_GET_TYPE(known_obj) == NMP_OBJECT_TYPE_IP_ADDRESS(IS_IPv4)); - known_address = NMP_OBJECT_CAST_IPX_ADDRESS(o); + known_address = NMP_OBJECT_CAST_IPX_ADDRESS(known_obj); lifetime = nmp_utils_lifetime_get(known_address->ax.timestamp, known_address->ax.lifetime, known_address->ax.preferred, - now, + &now, &preferred); nm_assert(lifetime > 0); + plat_obj = nm_platform_ip_address_get(self, addr_family, ifindex, known_address); + + if (plat_obj && nm_g_hash_table_contains(plat_addrs_to_delete, plat_obj)) { + /* This address exists, but it had the wrong priority earlier. We + * cannot just update it, we need to remove it first. */ + nm_platform_ip_address_delete(self, + addr_family, + ifindex, + NMP_OBJECT_CAST_IP_ADDRESS(plat_obj)); + plat_obj = NULL; + } + + if (plat_obj + && nm_platform_vtable_address.vx[IS_IPv4].address_cmp( + known_address, + NMP_OBJECT_CAST_IPX_ADDRESS(plat_obj), + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) + == 0) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + /* The object is already added. Skip update. */ + _LOG3T( + "address: skip updating IPv%c address: %s", + nm_utils_addr_family_to_char(addr_family), + nmp_object_to_string(known_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); + continue; + } + if (IS_IPv4) { if (!nm_platform_ip4_address_add( self, @@ -4172,9 +4437,8 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE, - known_address->a4.label)) { - /* ignore error, for unclear reasons. */ - } + known_address->a4.label)) + success = FALSE; } else { if (!nm_platform_ip6_address_add(self, ifindex, @@ -4184,11 +4448,11 @@ next_plat:; lifetime, preferred, IFA_F_NOPREFIXROUTE | known_address->a6.n_ifa_flags)) - return FALSE; + success = FALSE; } } - return TRUE; + return success; } gboolean @@ -4254,7 +4518,7 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, const int IS_IPv4 = NM_IS_IPv4(addr_family); const NMDedupMultiHeadEntry *head_entry; NMPLookup lookup; - GPtrArray *result; + GPtrArray *result = NULL; CList *iter; nmp_lookup_init_object(&lookup, NMP_OBJECT_TYPE_IP_ADDRESS(NM_IS_IPv4(addr_family)), ifindex); @@ -4264,8 +4528,6 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, if (!head_entry) return NULL; - result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; @@ -4275,13 +4537,12 @@ nm_platform_ip_address_get_prune_list(NMPlatform *self, continue; } + if (!result) + result = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nmp_object_unref); + g_ptr_array_add(result, (gpointer) nmp_object_ref(obj)); } - if (result->len == 0) { - g_ptr_array_unref(result); - return NULL; - } return result; } @@ -4292,7 +4553,7 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, NMIPRouteTableSyncMode route_table_sync) { NMPLookup lookup; - GPtrArray *routes_prune; + GPtrArray *routes_prune = NULL; const NMDedupMultiHeadEntry *head_entry; CList *iter; NMPlatformIP4Route rt_local4; @@ -4324,8 +4585,6 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, rt_local6.plen = 0; rt_mcast6.plen = 0; - routes_prune = g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); - c_list_for_each (iter, &head_entry->lst_entries_head) { const NMPObject *obj = c_list_entry(iter, NMDedupMultiEntry, lst_entries)->obj; const NMPlatformIPXRoute *rt = NMP_OBJECT_CAST_IPX_ROUTE(obj); @@ -4463,13 +4722,14 @@ nm_platform_ip_route_get_prune_list(NMPlatform *self, break; } + if (!routes_prune) { + routes_prune = + g_ptr_array_new_full(head_entry->len, (GDestroyNotify) nm_dedup_multi_obj_unref); + } + g_ptr_array_add(routes_prune, (gpointer) nmp_object_ref(obj)); } - if (routes_prune->len == 0) { - g_ptr_array_unref(routes_prune); - return NULL; - } return routes_prune; } @@ -4506,8 +4766,8 @@ nm_platform_ip_route_sync(NMPlatform *self, guint i; int i_type; gboolean success = TRUE; - char sbuf1[sizeof(_nm_utils_to_string_buffer)]; - char sbuf2[sizeof(_nm_utils_to_string_buffer)]; + char sbuf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; + char sbuf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_assert(NM_IS_PLATFORM(self)); nm_assert(ifindex > 0); @@ -4854,7 +5114,7 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) static int _ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) { - char sbuf[sizeof(_nm_utils_to_string_buffer)]; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; int ifindex; _CHECK_SELF(self, klass, FALSE); @@ -4906,7 +5166,8 @@ nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformI gboolean nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) { - int ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex; _CHECK_SELF(self, klass, FALSE); @@ -4914,7 +5175,7 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) case NMP_OBJECT_TYPE_ROUTING_RULE: _LOGD("%s: delete %s", NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); break; case NMP_OBJECT_TYPE_IP4_ROUTE: case NMP_OBJECT_TYPE_IP6_ROUTE: @@ -4923,7 +5184,7 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj) ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex; _LOG3D("%s: delete %s", NMP_OBJECT_GET_CLASS(obj)->obj_type_name, - nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); break; default: g_return_val_if_reached(FALSE); @@ -4941,6 +5202,7 @@ nm_platform_ip_route_get(NMPlatform *self, int oif_ifindex, NMPObject **out_route) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; nm_auto_nmpobj NMPObject *route = NULL; int result; char buf[NM_UTILS_INET_ADDRSTRLEN]; @@ -4977,7 +5239,7 @@ nm_platform_ip_route_get(NMPlatform *self, _LOGD("route: get IPv%c route for: %s succeeded: %s", nm_utils_addr_family_to_char(addr_family), inet_ntop(addr_family, address, buf, sizeof(buf)), - nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(route, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); NM_SET_OUT(out_route, g_steal_pointer(&route)); } return result; @@ -5004,6 +5266,7 @@ _ip4_dev_route_blacklist_timeout_ms_marked(gint64 timeout_msec) static gboolean _ip4_dev_route_blacklist_check_cb(gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatform *self = user_data; NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); GHashTableIter iter; @@ -5034,7 +5297,7 @@ again: continue; _LOGT("ip4-dev-route: delete %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); nm_platform_object_delete(self, p_obj); goto again; } @@ -5097,6 +5360,7 @@ _ip4_dev_route_blacklist_notify_route(NMPlatform *self, const NMPObject *obj) static gboolean _ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatform *self = user_data; NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE(self); GHashTableIter iter; @@ -5112,7 +5376,7 @@ _ip4_dev_route_blacklist_gc_timeout_handle(gpointer user_data) while (g_hash_table_iter_next(&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get(*p_timeout_ms)) { _LOGT("ip4-dev-route: cleanup %s", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); g_hash_table_iter_remove(&iter); } } @@ -5172,6 +5436,7 @@ nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, int ifindex, GPtrArray *ip4_dev_route_blacklist) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; NMPlatformPrivate *priv; GHashTableIter iter; const NMPObject *p_obj; @@ -5227,14 +5492,17 @@ nm_platform_ip4_dev_route_blacklist_set(NMPlatform *self, if (nmp_object_equal(p_obj, o)) { /* un-expire and reuse the entry. */ _LOGT("ip4-dev-route: register %s (update)", - nmp_object_to_string(p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(p_obj, + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); *p_timeout_ms = timeout_msec_val; continue; } } _LOGT("ip4-dev-route: register %s", - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); p_timeout_ms = g_slice_new(gint64); *p_timeout_ms = timeout_msec_val; g_hash_table_replace(priv->ip4_dev_route_blacklist_hash, @@ -5256,12 +5524,13 @@ nm_platform_routing_rule_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformRoutingRule *routing_rule) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; _CHECK_SELF(self, klass, -NME_BUG); g_return_val_if_fail(routing_rule, -NME_BUG); _LOGD("routing-rule: adding or updating: %s", - nm_platform_routing_rule_to_string(routing_rule, NULL, 0)); + nm_platform_routing_rule_to_string(routing_rule, sbuf, sizeof(sbuf))); return klass->routing_rule_add(self, flags, routing_rule); } @@ -5270,13 +5539,15 @@ nm_platform_routing_rule_add(NMPlatform *self, int nm_platform_qdisc_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformQdisc *qdisc) { - int ifindex = qdisc->ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex = qdisc->ifindex; _CHECK_SELF(self, klass, -NME_BUG); /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind * is undefined. */ - _LOG3D("adding or updating a qdisc: %s", nm_platform_qdisc_to_string(qdisc, NULL, 0)); + _LOG3D("adding or updating a qdisc: %s", + nm_platform_qdisc_to_string(qdisc, sbuf, sizeof(sbuf))); return klass->qdisc_add(self, flags, qdisc); } @@ -5294,13 +5565,15 @@ nm_platform_qdisc_delete(NMPlatform *self, int ifindex, guint32 parent, gboolean int nm_platform_tfilter_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformTfilter *tfilter) { - int ifindex = tfilter->ifindex; + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + int ifindex = tfilter->ifindex; _CHECK_SELF(self, klass, -NME_BUG); /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind * and tfilter.action.kind is undefined. */ - _LOG3D("adding or updating a tfilter: %s", nm_platform_tfilter_to_string(tfilter, NULL, 0)); + _LOG3D("adding or updating a tfilter: %s", + nm_platform_tfilter_to_string(tfilter, sbuf, sizeof(sbuf))); return klass->tfilter_add(self, flags, tfilter); } @@ -6251,7 +6524,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf "%s" /* label */ " src %s" "%s" /* a_acd_not_ready */ - "%s" /* a_assume_config_once */ "%s" /* a_force_commit */ "", s_address, @@ -6271,7 +6543,6 @@ nm_platform_ip4_address_to_string(const NMPlatformIP4Address *address, char *buf str_label, nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), address->a_acd_not_ready ? " ip4acd-not-ready" : "", - address->a_assume_config_once ? " assume-config-once" : "", address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; @@ -6392,7 +6663,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf buf, len, "%s/%d lft %s pref %s%s%s%s%s src %s" - "%s" /* a_assume_config_once */ "%s" /* a_force_commit */ "", s_address, @@ -6404,7 +6674,6 @@ nm_platform_ip6_address_to_string(const NMPlatformIP6Address *address, char *buf str_dev, _to_string_ifa_flags(address->n_ifa_flags, s_flags, sizeof(s_flags)), nmp_utils_ip_config_source_to_string(address->addr_source, s_source, sizeof(s_source)), - address->a_assume_config_once ? " assume-config-once" : "", address->a_force_commit ? " force-commit" : ""); g_free(str_peer); return buf; @@ -6508,7 +6777,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz "%s" /* initcwnd */ "%s" /* initrwnd */ "%s" /* mtu */ - "%s" /* r_assume_config_once */ "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6567,7 +6835,6 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->lock_mtu ? "lock " : "", route->mtu) : "", - route->r_assume_config_once ? " assume-config-once" : "", route->r_force_commit ? " force-commit" : ""); return buf; } @@ -6643,7 +6910,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz "%s" /* initrwnd */ "%s" /* mtu */ "%s" /* pref */ - "%s" /* r_assume_config_once */ "%s" /* r_force_commit */ "", nm_net_aux_rtnl_rtntype_n2a_maybe_buf(nm_platform_route_type_uncoerce(route->type_coerced), @@ -6706,7 +6972,6 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz " pref %s", nm_icmpv6_router_pref_to_string(route->rt_pref, str_pref2, sizeof(str_pref2))) : "", - route->r_assume_config_once ? " assume-config-once" : "", route->r_force_commit ? " force-commit" : ""); return buf; @@ -7746,8 +8011,8 @@ nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, * subnet (and thus also the primary/secondary role) is * preserved. */ - n1 = a1->address & _nm_utils_ip4_prefix_to_netmask(a1->plen); - n2 = a2->address & _nm_utils_ip4_prefix_to_netmask(a2->plen); + n1 = nm_utils_ip4_address_clear_host_address(a1->address, a1->plen); + n2 = nm_utils_ip4_address_clear_host_address(a2->address, a2->plen); NM_CMP_DIRECT_MEMCMP(&n1, &n2, sizeof(guint32)); return 0; } @@ -7770,6 +8035,25 @@ _address_pretty_sort_get_prio_6(const struct in6_addr *addr) return 6; } +static int +_address_cmp_expiry(const NMPlatformIPAddress *a, const NMPlatformIPAddress *b) +{ + guint32 lifetime_a; + guint32 lifetime_b; + guint32 preferred_a; + guint32 preferred_b; + gint32 now = 0; + + lifetime_a = + nmp_utils_lifetime_get(a->timestamp, a->lifetime, a->preferred, &now, &preferred_a); + lifetime_b = + nmp_utils_lifetime_get(b->timestamp, b->lifetime, b->preferred, &now, &preferred_b); + + NM_CMP_DIRECT(lifetime_a, lifetime_b); + NM_CMP_DIRECT(preferred_a, preferred_b); + return 0; +} + int nm_platform_ip6_address_pretty_sort_cmp(const NMPlatformIP6Address *a1, const NMPlatformIP6Address *a2, @@ -7843,71 +8127,122 @@ nm_platform_ip4_address_hash_update(const NMPlatformIP4Address *obj, NMHashState NM_HASH_COMBINE_BOOLS(guint8, obj->use_ip4_broadcast_address, obj->a_acd_not_ready, - obj->a_assume_config_once, obj->a_force_commit)); nm_hash_update_strarr(h, obj->label); } int -nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type) { NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD(a, b, address); NM_CMP_FIELD(a, b, plen); - NM_CMP_FIELD(a, b, peer_address); - NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); - if (a->use_ip4_broadcast_address) - NM_CMP_FIELD(a, b, broadcast_address); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_STR(a, b, label); - NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); - NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - return 0; + NM_CMP_FIELD(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv4 addresses, you can add the same local address with differing peer-address + * (IFA_ADDRESS), provided that their net-part differs. */ + NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a->peer_address, b->peer_address, a->plen); + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, peer_address); + NM_CMP_FIELD_STR(a, b, label); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE (and IFA_F_MANAGETEMPADDR for IPv6), + * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. + * There are thus no flags to compare for IPv4. */ + + NM_CMP_DIRECT(nm_platform_ip4_broadcast_address_from_addr(a), + nm_platform_ip4_broadcast_address_from_addr(b)); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, use_ip4_broadcast_address); + if (a->use_ip4_broadcast_address) + NM_CMP_FIELD(a, b, broadcast_address); + NM_CMP_FIELD_UNSAFE(a, b, a_acd_not_ready); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); } void nm_platform_ip6_address_hash_update(const NMPlatformIP6Address *obj, NMHashState *h) { - nm_hash_update_vals( - h, - obj->ifindex, - obj->addr_source, - obj->timestamp, - obj->lifetime, - obj->preferred, - obj->n_ifa_flags, - obj->plen, - obj->address, - obj->peer_address, - NM_HASH_COMBINE_BOOLS(guint8, obj->a_assume_config_once, obj->a_force_commit)); + nm_hash_update_vals(h, + obj->ifindex, + obj->addr_source, + obj->timestamp, + obj->lifetime, + obj->preferred, + obj->n_ifa_flags, + obj->plen, + obj->address, + obj->peer_address, + NM_HASH_COMBINE_BOOLS(guint8, obj->a_force_commit)); } int -nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type) { const struct in6_addr *p_a, *p_b; NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); - NM_CMP_FIELD_MEMCMP(a, b, address); - NM_CMP_FIELD(a, b, plen); - p_a = nm_platform_ip6_address_get_peer(a); - p_b = nm_platform_ip6_address_get_peer(b); - NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); - NM_CMP_FIELD(a, b, addr_source); - NM_CMP_FIELD(a, b, timestamp); - NM_CMP_FIELD(a, b, lifetime); - NM_CMP_FIELD(a, b, preferred); - NM_CMP_FIELD(a, b, n_ifa_flags); - NM_CMP_FIELD_UNSAFE(a, b, a_assume_config_once); - NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); - return 0; + NM_CMP_FIELD_IN6ADDR(a, b, address); + + switch (cmp_type) { + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID: + /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ + return 0; + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY: + case NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL: + NM_CMP_FIELD(a, b, plen); + p_a = nm_platform_ip6_address_get_peer(a); + p_b = nm_platform_ip6_address_get_peer(b); + NM_CMP_DIRECT_MEMCMP(p_a, p_b, sizeof(*p_a)); + if (cmp_type == NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY) { + NM_CMP_RETURN(_address_cmp_expiry((const NMPlatformIPAddress *) a, + (const NMPlatformIPAddress *) b)); + + /* Most flags are set by kernel. We only compare the ones that + * NetworkManager actively sets. + * + * NM actively only sets IFA_F_NOPREFIXROUTE and IFA_F_MANAGETEMPADDR, + * where nm_platform_ip_address_sync() always sets IFA_F_NOPREFIXROUTE. + * We thus only care about IFA_F_MANAGETEMPADDR. */ + NM_CMP_DIRECT(a->n_ifa_flags & IFA_F_MANAGETEMPADDR, + b->n_ifa_flags & IFA_F_MANAGETEMPADDR); + } else { + NM_CMP_FIELD(a, b, timestamp); + NM_CMP_FIELD(a, b, lifetime); + NM_CMP_FIELD(a, b, preferred); + NM_CMP_FIELD(a, b, n_ifa_flags); + NM_CMP_FIELD(a, b, addr_source); + NM_CMP_FIELD_UNSAFE(a, b, a_force_commit); + } + return 0; + } + return nm_assert_unreachable_val(0); } void @@ -8007,7 +8342,7 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->initrwnd, obj->mtu, obj->r_rtm_flags, - NM_HASH_COMBINE_BOOLS(guint16, + NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any, obj->lock_window, @@ -8015,7 +8350,6 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once, obj->r_force_commit)); break; } @@ -8106,10 +8440,8 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, initcwnd); NM_CMP_FIELD(a, b, initrwnd); NM_CMP_FIELD(a, b, mtu); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); - } break; } return 0; @@ -8202,7 +8534,6 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->lock_initcwnd, obj->lock_initrwnd, obj->lock_mtu, - obj->r_assume_config_once, obj->r_force_commit), obj->window, obj->cwnd, @@ -8286,10 +8617,8 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_DIRECT(_route_pref_normalize(a->rt_pref), _route_pref_normalize(b->rt_pref)); else NM_CMP_FIELD(a, b, rt_pref); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) { - NM_CMP_FIELD_UNSAFE(a, b, r_assume_config_once); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) NM_CMP_FIELD_UNSAFE(a, b, r_force_commit); - } break; } return 0; @@ -8651,9 +8980,11 @@ log_link(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: link %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_link_to_string(device, NULL, 0)); + nm_platform_link_to_string(device, sbuf, sizeof(sbuf))); } static void @@ -8664,9 +8995,11 @@ log_ip4_address(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_address_to_string(address, NULL, 0)); + nm_platform_ip4_address_to_string(address, sbuf, sizeof(sbuf))); } static void @@ -8677,9 +9010,11 @@ log_ip6_address(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_address_to_string(address, NULL, 0)); + nm_platform_ip6_address_to_string(address, sbuf, sizeof(sbuf))); } static void @@ -8690,9 +9025,11 @@ log_ip4_route(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, NULL, 0)); + nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))); } static void @@ -8703,9 +9040,11 @@ log_ip6_route(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, NULL, 0)); + nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); } static void @@ -8716,10 +9055,12 @@ log_routing_rule(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + /* routing rules don't have an ifindex. We probably should refactor the signals that are emitted for platform changes. */ _LOG3D("signal: rt-rule %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_routing_rule_to_string(routing_rule, NULL, 0)); + nm_platform_routing_rule_to_string(routing_rule, sbuf, sizeof(sbuf))); } static void @@ -8730,9 +9071,11 @@ log_qdisc(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_qdisc_to_string(qdisc, NULL, 0)); + nm_platform_qdisc_to_string(qdisc, sbuf, sizeof(sbuf))); } static void @@ -8743,9 +9086,11 @@ log_tfilter(NMPlatform *self, NMPlatformSignalChangeType change_type, gpointer user_data) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + _LOG3D("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_tfilter_to_string(tfilter, NULL, 0)); + nm_platform_tfilter_to_string(tfilter, sbuf, sizeof(sbuf))); } /*****************************************************************************/ @@ -8756,6 +9101,7 @@ nm_platform_cache_update_emit_signal(NMPlatform *self, const NMPObject *obj_old, const NMPObject *obj_new) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; gboolean visible_new; gboolean visible_old; const NMPObject *o; @@ -8818,7 +9164,7 @@ nm_platform_cache_update_emit_signal(NMPlatform *self, _LOG3t("emit signal %s %s: %s", klass->signal_type, nm_platform_signal_change_type_to_string((NMPlatformSignalChangeType) cache_op), - nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); nmp_object_ref(o); g_signal_emit(self, @@ -8863,6 +9209,37 @@ nm_platform_netns_push(NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ +const _NMPlatformVTableAddressUnion nm_platform_vtable_address = { + .v4 = + { + .is_ip4 = TRUE, + .obj_type = NMP_OBJECT_TYPE_IP4_ADDRESS, + .addr_family = AF_INET, + .sizeof_address = sizeof(NMPlatformIP4Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip4_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip4_address_to_string, + }, + .v6 = + { + .is_ip4 = FALSE, + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .addr_family = AF_INET6, + .sizeof_address = sizeof(NMPlatformIP6Address), + .address_cmp = + (int (*)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type)) nm_platform_ip6_address_cmp, + .address_to_string = (const char *(*) (const NMPlatformIPXAddress *address, + char *buf, + gsize len)) nm_platform_ip6_address_to_string, + }, +}; + const _NMPlatformVTableRouteUnion nm_platform_vtable_route = { .v4 = { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 6f5cd524..246d6ff6 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -95,6 +95,14 @@ typedef enum { } NMPNlmFlags; typedef enum { + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_SEMANTICALLY, + + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL, +} NMPlatformIPAddressCmpType; + +typedef enum { /* compare fields which kernel considers as similar routes. * It is a looser comparisong then NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID * and means that `ip route add` would fail to add two routes @@ -259,17 +267,17 @@ struct _NMPlatformLink { bool initialized : 1; }; -typedef enum { /*< skip >*/ - NM_PLATFORM_SIGNAL_ID_NONE, - NM_PLATFORM_SIGNAL_ID_LINK, - NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, - NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, - NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, - NM_PLATFORM_SIGNAL_ID_QDISC, - NM_PLATFORM_SIGNAL_ID_TFILTER, - _NM_PLATFORM_SIGNAL_ID_LAST, +typedef enum { + NM_PLATFORM_SIGNAL_ID_NONE, + NM_PLATFORM_SIGNAL_ID_LINK, + NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, + NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, + NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, + NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, + NM_PLATFORM_SIGNAL_ID_ROUTING_RULE, + NM_PLATFORM_SIGNAL_ID_QDISC, + NM_PLATFORM_SIGNAL_ID_TFILTER, + _NM_PLATFORM_SIGNAL_ID_LAST, } NMPlatformSignalIdType; guint _nm_platform_signal_id_get(NMPlatformSignalIdType signal_type); @@ -323,8 +331,6 @@ typedef enum { /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be * used by the upper layers which use NMPlatformIPRoute to track addresses that * should be configured. */ \ - /* Whether the address is should be configured once during assume. */ \ - bool a_assume_config_once : 1; \ bool a_force_commit : 1; \ \ guint8 plen; \ @@ -468,12 +474,9 @@ typedef union { * This field overrides "table_coerced" field. If "table_any" is true, then * the "table_coerced" field is ignored (unlike for the metric). */ \ bool table_any : 1; \ - \ /* Meta flags not honored by NMPlatform (netlink code). Instead, they can be * used by the upper layers which use NMPlatformIPRoute to track routes that * should be configured. */ \ - /* Whether the route is should be configured once during assume. */ \ - bool r_assume_config_once : 1; \ /* Whether the route should be committed even if it was removed externally. */ \ bool r_force_commit : 1; \ \ @@ -776,10 +779,31 @@ typedef struct { #undef __NMPlatformObjWithIfindex_COMMON typedef struct { - gboolean is_ip4; + bool is_ip4; + NMPObjectType obj_type; + gint8 addr_family; + guint8 sizeof_address; + int (*address_cmp)(const NMPlatformIPXAddress *a, + const NMPlatformIPXAddress *b, + NMPlatformIPAddressCmpType cmp_type); + const char *(*address_to_string)(const NMPlatformIPXAddress *address, char *buf, gsize len); +} NMPlatformVTableAddress; + +typedef union { + struct { + NMPlatformVTableAddress v6; + NMPlatformVTableAddress v4; + }; + NMPlatformVTableAddress vx[2]; +} _NMPlatformVTableAddressUnion; + +extern const _NMPlatformVTableAddressUnion nm_platform_vtable_address; + +typedef struct { + bool is_ip4; + gint8 addr_family; + guint8 sizeof_route; NMPObjectType obj_type; - int addr_family; - gsize sizeof_route; int (*route_cmp)(const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); @@ -1017,6 +1041,14 @@ typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data); /*****************************************************************************/ +typedef struct _NMPlatformCsmeConnInfo { + guint8 ssid[32]; + guint32 channel; + NMEtherAddr addr; + guint8 sta_cipher; + guint8 auth_mode; +} NMPlatformCsmeConnInfo; + typedef enum { NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_L3MDEV, NM_PLATFORM_KERNEL_SUPPORT_TYPE_FRA_UID_RANGE, @@ -1105,6 +1137,9 @@ typedef struct { guint32 mtu, gconstpointer extra_data, const NMPlatformLink **out_link); + + int (*link_change)(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data); + gboolean (*link_delete)(NMPlatform *self, int ifindex); gboolean (*link_refresh)(NMPlatform *self, int ifindex); gboolean (*link_set_netns)(NMPlatform *self, int ifindex, int netns_fd); @@ -1204,6 +1239,10 @@ typedef struct { gboolean (*wifi_set_wake_on_wlan)(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan wowl); + gboolean (*wifi_get_csme_conn_info)(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info); + gboolean (*wifi_get_device_from_csme)(NMPlatform *self, int ifindex); guint32 (*mesh_get_channel)(NMPlatform *self, int ifindex); gboolean (*mesh_set_channel)(NMPlatform *self, int ifindex, guint32 channel); @@ -1411,7 +1450,11 @@ _nm_platform_link_get_inet6_addr_gen_mode(const NMPlatformLink *pllink) static inline gboolean nm_platform_route_type_is_nodev(guint8 type) { - return NM_IN_SET(type, 6 /* RTN_BLACKHOLE */, 7 /* RTN_UNREACHABLE */, 8 /* RTN_PROHIBIT */); + return NM_IN_SET(type, + 6 /* RTN_BLACKHOLE */, + 7 /* RTN_UNREACHABLE */, + 8 /* RTN_PROHIBIT */, + 9 /* RTN_THROW */); } /** @@ -1570,6 +1613,9 @@ int nm_platform_link_add(NMPlatform *self, gconstpointer extra_data, const NMPlatformLink **out_link); +int +nm_platform_link_change(NMPlatform *self, NMLinkType type, int ifindex, gconstpointer extra_data); + static inline int nm_platform_link_veth_add(NMPlatform *self, const char *name, @@ -1606,6 +1652,12 @@ nm_platform_link_bridge_add(NMPlatform *self, } static inline int +nm_platform_link_bridge_change(NMPlatform *self, int ifindex, const NMPlatformLnkBridge *props) +{ + return nm_platform_link_change(self, NM_LINK_TYPE_BRIDGE, ifindex, props); +} + +static inline int nm_platform_link_bond_add(NMPlatform *self, const char *name, const NMPlatformLink **out_link) { return nm_platform_link_add(self, NM_LINK_TYPE_BOND, name, 0, NULL, 0, 0, NULL, out_link); @@ -2024,6 +2076,10 @@ void nm_platform_wifi_indicate_addressing_running(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan nm_platform_wifi_get_wake_on_wlan(NMPlatform *self, int ifindex); gboolean nm_platform_wifi_set_wake_on_wlan(NMPlatform *self, int ifindex, _NMSettingWirelessWakeOnWLan wowl); +gboolean nm_platform_wifi_get_csme_conn_info(NMPlatform *self, + int ifindex, + NMPlatformCsmeConnInfo *out_conn_info); +gboolean nm_platform_wifi_get_device_from_csme(NMPlatform *self, int ifindex); guint32 nm_platform_mesh_get_channel(NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel(NMPlatform *self, int ifindex, guint32 channel); @@ -2038,6 +2094,11 @@ gboolean nm_platform_wpan_set_channel(NMPlatform *self, int ifindex, guint8 page void nm_platform_ip4_address_set_addr(NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); const struct in6_addr *nm_platform_ip6_address_get_peer(const NMPlatformIP6Address *addr); +const NMPObject *nm_platform_ip_address_get(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (NMPlatformIPAddress *) */ needle); + const NMPlatformIP4Address *nm_platform_ip4_address_get(NMPlatform *self, int ifindex, in_addr_t address, @@ -2097,6 +2158,29 @@ gboolean nm_platform_ip4_address_delete(NMPlatform *self, gboolean nm_platform_ip6_address_delete(NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); +static inline gboolean +nm_platform_ip_address_delete(NMPlatform *self, + int addr_family, + int ifindex, + gconstpointer /* (const NMPlatformIPAddress *) */ addr) +{ + if (NM_IS_IPv4(addr_family)) { + const NMPlatformIP4Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip4_address_delete(self, ifindex, a->address, a->plen, a->peer_address); + } else { + const NMPlatformIP6Address *a = addr; + + if (ifindex <= 0) + ifindex = a->ifindex; + + return nm_platform_ip6_address_delete(self, ifindex, a->address, a->plen); + } +} + gboolean nm_platform_ip_address_sync(NMPlatform *self, int addr_family, int ifindex, @@ -2288,8 +2372,24 @@ int nm_platform_lnk_vlan_cmp(const NMPlatformLnkVlan *a, const NMPlatformLnkVlan int nm_platform_lnk_vrf_cmp(const NMPlatformLnkVrf *a, const NMPlatformLnkVrf *b); int nm_platform_lnk_vxlan_cmp(const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformLnkWireGuard *b); -int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); -int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); +int nm_platform_ip4_address_cmp(const NMPlatformIP4Address *a, + const NMPlatformIP4Address *b, + NMPlatformIPAddressCmpType cmp_type); +int nm_platform_ip6_address_cmp(const NMPlatformIP6Address *a, + const NMPlatformIP6Address *b, + NMPlatformIPAddressCmpType cmp_type); + +static inline int +nm_platform_ip4_address_cmp_full(const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +{ + return nm_platform_ip4_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} + +static inline int +nm_platform_ip6_address_cmp_full(const NMPlatformIP6Address *a, const NMPlatformIP6Address *b) +{ + return nm_platform_ip6_address_cmp(a, b, NM_PLATFORM_IP_ADDRESS_CMP_TYPE_FULL); +} int nm_platform_ip4_address_pretty_sort_cmp(const NMPlatformIP4Address *a1, const NMPlatformIP4Address *a2); diff --git a/src/libnm-platform/nmp-base.h b/src/libnm-platform/nmp-base.h index a80fd4d3..48631688 100644 --- a/src/libnm-platform/nmp-base.h +++ b/src/libnm-platform/nmp-base.h @@ -110,7 +110,7 @@ typedef struct _NMPlatformIP6Route NMPlatformIP6Route; typedef struct _NMPlatformLink NMPlatformLink; typedef struct _NMPObject NMPObject; -typedef enum { +typedef enum _nm_packed { NMP_OBJECT_TYPE_UNKNOWN, NMP_OBJECT_TYPE_LINK, diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index d518e6e5..6c177b4c 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -799,7 +799,7 @@ nmp_object_new_link(int ifindex) /*****************************************************************************/ -static void +static NMPObject * _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) { nm_assert(obj); @@ -812,25 +812,13 @@ _nmp_object_stackinit_from_class(NMPObject *obj, const NMPClass *klass) ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, }, }; + return obj; } static NMPObject * _nmp_object_stackinit_from_type(NMPObject *obj, NMPObjectType obj_type) { - const NMPClass *klass; - - nm_assert(obj); - klass = nmp_class_from_type(obj_type); - nm_assert(klass); - - *obj = (NMPObject){ - .parent = - { - .klass = (const NMDedupMultiObjClass *) klass, - ._ref_count = NM_OBJ_REF_COUNT_STACKINIT, - }, - }; - return obj; + return _nmp_object_stackinit_from_class(obj, nmp_class_from_type(obj_type)); } const NMPObject * @@ -901,7 +889,7 @@ nmp_object_to_string(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; if (!nm_utils_to_string_buffer_init_null(obj, &buf, &buf_size)) return buf; @@ -992,7 +980,7 @@ _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; char *b; gsize l; @@ -1056,7 +1044,7 @@ _vt_cmd_obj_to_string_lnk_wireguard(const NMPObject *obj, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof(_nm_utils_to_string_buffer)]; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; char *b; guint i; @@ -1523,20 +1511,21 @@ nmp_object_id_cmp(const NMPObject *obj1, const NMPObject *obj2) _vt_cmd_plobj_id_cmp(link, NMPlatformLink, { NM_CMP_FIELD(obj1, obj2, ifindex); }); -_vt_cmd_plobj_id_cmp(ip4_address, NMPlatformIP4Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - NM_CMP_FIELD(obj1, obj2, plen); - NM_CMP_FIELD(obj1, obj2, address); - /* for IPv4 addresses, you can add the same local address with differing peer-address - * (IFA_ADDRESS), provided that their net-part differs. */ - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(obj1->peer_address, obj2->peer_address, obj1->plen); -}); +static int +_vt_cmd_plobj_id_cmp_ip4_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip4_address_cmp((const NMPlatformIP4Address *) obj1, + (const NMPlatformIP4Address *) obj2, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); +} -_vt_cmd_plobj_id_cmp(ip6_address, NMPlatformIP6Address, { - NM_CMP_FIELD(obj1, obj2, ifindex); - /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ - NM_CMP_FIELD_IN6ADDR(obj1, obj2, address); -}); +static int +_vt_cmd_plobj_id_cmp_ip6_address(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip6_address_cmp((const NMPlatformIP6Address *) obj1, + (const NMPlatformIP6Address *) obj2, + NM_PLATFORM_IP_ADDRESS_CMP_TYPE_ID); +} _vt_cmd_plobj_id_cmp(qdisc, NMPlatformQdisc, { NM_CMP_FIELD(obj1, obj2, ifindex); @@ -1551,24 +1540,24 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { static int _vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip4_route_cmp((NMPlatformIP4Route *) obj1, - (NMPlatformIP4Route *) obj2, + return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, + (const NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } static int _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip6_route_cmp((NMPlatformIP6Route *) obj1, - (NMPlatformIP6Route *) obj2, + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, + (const NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } static int _vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_routing_rule_cmp((NMPlatformRoutingRule *) obj1, - (NMPlatformRoutingRule *) obj2, + return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, + (const NMPlatformRoutingRule *) obj2, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID); } @@ -3158,28 +3147,29 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_address_to_string, .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip4_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_address_cmp_full, + }, + [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = + { + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, + .sizeof_data = sizeof(NMPObjectIP6Address), + .sizeof_public = sizeof(NMPlatformIP6Address), + .obj_type_name = "ip6-address", + .addr_family = AF_INET6, + .rtm_gettype = RTM_GETADDR, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, + .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, + .supported_cache_ids = _supported_cache_ids_ipx_address, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, + .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, + .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, + .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, + .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, + .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, + .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, + .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp_full, }, - [NMP_OBJECT_TYPE_IP6_ADDRESS - - 1] = {.parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP6_ADDRESS, - .sizeof_data = sizeof(NMPObjectIP6Address), - .sizeof_public = sizeof(NMPlatformIP6Address), - .obj_type_name = "ip6-address", - .addr_family = AF_INET6, - .rtm_gettype = RTM_GETADDR, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, - .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_address, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, - .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_address, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip6_address, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_address_to_string, - .cmd_plobj_hash_update = - (CmdPlobjHashUpdateFunc) nm_platform_ip6_address_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_address_cmp}, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index 784dcc2d..d6b88ba2 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -91,17 +91,17 @@ typedef struct _NMPWireGuardPeer { /*****************************************************************************/ -typedef enum { /*< skip >*/ - NMP_OBJECT_TO_STRING_ID, - NMP_OBJECT_TO_STRING_PUBLIC, - NMP_OBJECT_TO_STRING_ALL, +typedef enum { + NMP_OBJECT_TO_STRING_ID, + NMP_OBJECT_TO_STRING_PUBLIC, + NMP_OBJECT_TO_STRING_ALL, } NMPObjectToStringMode; -typedef enum { /*< skip >*/ - NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, - NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, - NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, - NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, +typedef enum { + NMP_CACHE_OPS_UNCHANGED = NM_PLATFORM_SIGNAL_NONE, + NMP_CACHE_OPS_ADDED = NM_PLATFORM_SIGNAL_ADDED, + NMP_CACHE_OPS_UPDATED = NM_PLATFORM_SIGNAL_CHANGED, + NMP_CACHE_OPS_REMOVED = NM_PLATFORM_SIGNAL_REMOVED, } NMPCacheOpsType; /* The NMPCacheIdType are the different index types. @@ -1098,21 +1098,6 @@ nm_platform_lookup_object_by_addr_family(NMPlatform *platform, /*****************************************************************************/ static inline gboolean -nmp_object_get_assume_config_once(const NMPObject *obj) -{ - switch (NMP_OBJECT_GET_TYPE(obj)) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - return NMP_OBJECT_CAST_IP_ADDRESS(obj)->a_assume_config_once; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - return NMP_OBJECT_CAST_IP_ROUTE(obj)->r_assume_config_once; - default: - return nm_assert_unreachable_val(FALSE); - } -} - -static inline gboolean nmp_object_get_force_commit(const NMPObject *obj) { switch (NMP_OBJECT_GET_TYPE(obj)) { diff --git a/src/libnm-platform/nmp-route-manager.c b/src/libnm-platform/nmp-route-manager.c index c31c9806..842301b1 100644 --- a/src/libnm-platform/nmp-route-manager.c +++ b/src/libnm-platform/nmp-route-manager.c @@ -397,14 +397,17 @@ nmp_route_manager_track(NMPRouteManager *self, _track_data_assert(track_data, TRUE); if (changed) { - _LOGD("track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", - NM_HASH_OBFUSCATE_PTR(track_data->user_tag), - (track_data->track_priority_val == 0 - ? "" - : (track_data->track_priority_present ? "+" : "-")), - (guint) track_data->track_priority_val, - NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + _LOGD( + "track [" NM_HASH_OBFUSCATE_PTR_FMT ",%s%u] %s \"%s\"", + NM_HASH_OBFUSCATE_PTR(track_data->user_tag), + (track_data->track_priority_val == 0 + ? "" + : (track_data->track_priority_present ? "+" : "-")), + (guint) track_data->track_priority_val, + NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); } return changed || changed_untrack; @@ -416,6 +419,7 @@ _track_data_untrack(NMPRouteManager *self, gboolean remove_user_tag_data, gboolean make_owned_by_us) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; TrackObjData *obj_data; nm_assert(NMP_IS_ROUTE_MANAGER(self)); @@ -426,7 +430,7 @@ _track_data_untrack(NMPRouteManager *self, _LOGD("untrack [" NM_HASH_OBFUSCATE_PTR_FMT "] %s \"%s\"", NM_HASH_OBFUSCATE_PTR(track_data->user_tag), NMP_OBJECT_GET_CLASS(track_data->obj)->obj_type_name, - nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(track_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); #if NM_MORE_ASSERTS { @@ -552,6 +556,7 @@ nmp_route_manager_untrack_all(NMPRouteManager *self, void nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean keep_deleted) { + char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; const NMDedupMultiHeadEntry *pl_head_entry; NMDedupMultiIter pl_iter; const NMPObject *plobj; @@ -608,7 +613,7 @@ nmp_route_manager_sync(NMPRouteManager *self, NMPObjectType obj_type, gboolean k if (keep_deleted) { _LOGD("forget/leak object added by us: %s \"%s\"", NMP_OBJECT_GET_CLASS(plobj)->obj_type_name, - nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nmp_object_to_string(plobj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); continue; } diff --git a/src/libnm-platform/tests/test-nm-platform.c b/src/libnm-platform/tests/test-nm-platform.c index d4de0dd5..9ac69bde 100644 --- a/src/libnm-platform/tests/test-nm-platform.c +++ b/src/libnm-platform/tests/test-nm-platform.c @@ -131,11 +131,13 @@ test_nmp_link_mode_all_advertised_modes_bits(void) for (i = 0; i < (int) G_N_ELEMENTS(_nmp_link_mode_all_advertised_modes); i++) { if (flags[i] != _nmp_link_mode_all_advertised_modes[i]) { + NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER g_error("_nmp_link_mode_all_advertised_modes[%d] should be 0x%0x but is 0x%0x " "(according to the bits in _nmp_link_mode_all_advertised_modes_bits)", i, flags[i], _nmp_link_mode_all_advertised_modes[i]); + NM_PRAGMA_WARNING_REENABLE } } } diff --git a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c index 3906384b..475b8a39 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-nl80211.c @@ -15,6 +15,8 @@ #include <linux/nl80211.h> #include <linux/if.h> +#include "linux-headers/nl80211-vnd-intel.h" + #include "libnm-log-core/nm-logging.h" #include "libnm-platform/nm-netlink.h" #include "nm-wifi-utils-private.h" @@ -384,8 +386,10 @@ wifi_nl80211_find_freq(NMWifiUtils *data, const guint32 *freqs) int i; int j; - for (i = 0; i < self->num_freqs; i++) { - for (j = 0; freqs[j] != 0; j++) { + /* It's important to check the values in the order of @freqs, because + * that array might be sorted to contain preferred frequencies first. */ + for (j = 0; freqs[j] != 0; j++) { + for (i = 0; i < self->num_freqs; i++) { if (self->freqs[i] == freqs[j]) return freqs[j]; } @@ -597,7 +601,7 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) struct nlattr *nl_freq; int rem_freq; int rem_band; - int freq_idx; + guint num_alloc; #ifdef NL80211_FREQUENCY_ATTR_NO_IR G_STATIC_ASSERT_EXPR(NL80211_FREQUENCY_ATTR_PASSIVE_SCAN == NL80211_FREQUENCY_ATTR_NO_IR @@ -651,8 +655,10 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) } } - /* Find number of supported frequencies */ + /* Read supported frequencies */ + num_alloc = 32; info->num_freqs = 0; + info->freqs = g_new(guint32, num_alloc); nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) @@ -665,38 +671,28 @@ nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg) if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; - info->num_freqs++; - } - } - - /* Read supported frequencies */ - info->freqs = g_malloc0(sizeof(guint32) * info->num_freqs); - - freq_idx = 0; - nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { - if (nla_parse_nested_arr(tb_band, nl_band, NULL) < 0) - return NL_SKIP; - - nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { - if (nla_parse_nested_arr(tb_freq, nl_freq, freq_policy) < 0) - continue; - - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) continue; - info->freqs[freq_idx] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + if (info->num_freqs >= num_alloc) { + num_alloc *= 2; + info->freqs = g_renew(guint32, info->freqs, num_alloc); + } + info->freqs[info->num_freqs] = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_VALID; - if (info->freqs[freq_idx] > 2400 && info->freqs[freq_idx] < 2500) + if (info->freqs[info->num_freqs] > 2400 && info->freqs[info->num_freqs] < 2500) info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_2GHZ; - if (info->freqs[freq_idx] > 4900 && info->freqs[freq_idx] < 6000) + if (info->freqs[info->num_freqs] > 4900 && info->freqs[info->num_freqs] < 6000) info->caps |= _NM_WIFI_DEVICE_CAP_FREQ_5GHZ; - freq_idx++; + info->num_freqs++; } } + info->freqs = g_renew(guint32, info->freqs, info->num_freqs); + /* Read security/encryption support */ if (tb[NL80211_ATTR_CIPHER_SUITES]) { guint32 *ciphers = nla_data(tb[NL80211_ATTR_CIPHER_SUITES]); @@ -815,6 +811,111 @@ nla_put_failure: g_return_val_if_reached(FALSE); } +struct nl80211_csme_conn_info { + NMWifiUtilsNl80211 *self; + NMPlatformCsmeConnInfo *conn_info; +}; + +static int +nl80211_csme_conn_event_handler(struct nl_msg *msg, void *arg) +{ + struct nl80211_csme_conn_info *info = arg; + NMPlatformCsmeConnInfo *out_conn_info = info->conn_info; + NMWifiUtilsNl80211 *self = info->self; + struct genlmsghdr *gnlh = (void *) nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *data; + struct nlattr *attrs[NUM_IWL_MVM_VENDOR_ATTR]; + int err; + + static const struct nla_policy iwl_vendor_policy[NUM_IWL_MVM_VENDOR_ATTR] = { + [IWL_MVM_VENDOR_ATTR_AUTH_MODE] = {.type = NLA_U32}, + [IWL_MVM_VENDOR_ATTR_SSID] = {.type = NLA_UNSPEC, .maxlen = NM_IW_ESSID_MAX_SIZE}, + [IWL_MVM_VENDOR_ATTR_STA_CIPHER] = {.type = NLA_U32}, + [IWL_MVM_VENDOR_ATTR_CHANNEL_NUM] = {.type = NLA_U8}, + [IWL_MVM_VENDOR_ATTR_ADDR] = {.type = NLA_UNSPEC, .minlen = ETH_ALEN, .maxlen = ETH_ALEN}, + }; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); + data = tb[NL80211_ATTR_VENDOR_DATA]; + + *out_conn_info = (NMPlatformCsmeConnInfo){}; + + err = nla_parse_nested(attrs, MAX_IWL_MVM_VENDOR_ATTR, data, iwl_vendor_policy); + if (err) { + _LOGD("IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO Failed to parse CSME connection info: %s", + nm_strerror(err)); + return -EINVAL; + } + + if (attrs[IWL_MVM_VENDOR_ATTR_AUTH_MODE]) + out_conn_info->auth_mode = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_AUTH_MODE]); + + if (attrs[IWL_MVM_VENDOR_ATTR_SSID]) + memcpy(out_conn_info->ssid, + nla_data(attrs[IWL_MVM_VENDOR_ATTR_SSID]), + nla_len(attrs[IWL_MVM_VENDOR_ATTR_SSID])); + + if (attrs[IWL_MVM_VENDOR_ATTR_STA_CIPHER]) + out_conn_info->sta_cipher = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_STA_CIPHER]); + + if (attrs[IWL_MVM_VENDOR_ATTR_CHANNEL_NUM]) + out_conn_info->channel = nla_get_u8(attrs[IWL_MVM_VENDOR_ATTR_CHANNEL_NUM]); + + if (attrs[IWL_MVM_VENDOR_ATTR_ADDR]) + memcpy(&out_conn_info->addr, + nla_data(attrs[IWL_MVM_VENDOR_ATTR_ADDR]), + sizeof(out_conn_info->addr)); + + return NL_SKIP; +} + +static gboolean +wifi_nl80211_intel_vnd_get_csme_conn_info(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + struct nl80211_csme_conn_info conn_info = { + .self = self, + .conn_info = out_conn_info, + }; + + msg = nl80211_alloc_msg(self, NL80211_CMD_VENDOR, 0); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, INTEL_OUI); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO); + + err = nl80211_send_and_recv(self, msg, nl80211_csme_conn_event_handler, &conn_info); + if (err < 0) + _LOGD("IWL_MVM_VENDOR_CMD_GET_CSME_CONN_INFO request failed: %s", nm_strerror(err)); + + return err >= 0; + +nla_put_failure: + g_return_val_if_reached(FALSE); +} + +static gboolean +wifi_nl80211_intel_vnd_get_device_from_csme(NMWifiUtils *data) +{ + NMWifiUtilsNl80211 *self = (NMWifiUtilsNl80211 *) data; + nm_auto_nlmsg struct nl_msg *msg = NULL; + int err; + + msg = nl80211_alloc_msg(self, NL80211_CMD_VENDOR, 0); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, INTEL_OUI); + NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP); + + err = nl80211_send_and_recv(self, msg, NULL, NULL); + if (err < 0) + _LOGD("IWL_MVM_VENDOR_CMD_HOST_GET_OWNERSHIP request failed: %s", nm_strerror(err)); + + return err >= 0; + +nla_put_failure: + g_return_val_if_reached(FALSE); +} + static void nm_wifi_utils_nl80211_init(NMWifiUtilsNl80211 *self) {} @@ -839,6 +940,8 @@ nm_wifi_utils_nl80211_class_init(NMWifiUtilsNl80211Class *klass) wifi_utils_class->get_mesh_channel = wifi_nl80211_get_mesh_channel; wifi_utils_class->set_mesh_channel = wifi_nl80211_set_mesh_channel; wifi_utils_class->set_mesh_ssid = wifi_nl80211_set_mesh_ssid; + wifi_utils_class->get_csme_conn_info = wifi_nl80211_intel_vnd_get_csme_conn_info; + wifi_utils_class->get_device_from_csme = wifi_nl80211_intel_vnd_get_device_from_csme; } NMWifiUtils * diff --git a/src/libnm-platform/wifi/nm-wifi-utils-private.h b/src/libnm-platform/wifi/nm-wifi-utils-private.h index 7461b65e..7fe157f2 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-private.h +++ b/src/libnm-platform/wifi/nm-wifi-utils-private.h @@ -7,6 +7,7 @@ #define __WIFI_UTILS_PRIVATE_H__ #include "nm-wifi-utils.h" +#include "libnm-platform/nm-platform.h" typedef struct { GObjectClass parent; @@ -53,6 +54,10 @@ typedef struct { gboolean (*set_mesh_ssid)(NMWifiUtils *data, const guint8 *ssid, gsize len); gboolean (*indicate_addressing_running)(NMWifiUtils *data, gboolean running); + + gboolean (*get_csme_conn_info)(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info); + + gboolean (*get_device_from_csme)(NMWifiUtils *data); } NMWifiUtilsClass; struct NMWifiUtils { diff --git a/src/libnm-platform/wifi/nm-wifi-utils-wext.c b/src/libnm-platform/wifi/nm-wifi-utils-wext.c index 8d0e6ed0..678d71fe 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils-wext.c +++ b/src/libnm-platform/wifi/nm-wifi-utils-wext.c @@ -255,8 +255,10 @@ wifi_wext_find_freq(NMWifiUtils *data, const guint32 *freqs) guint i; guint j; - for (i = 0; i < wext->num_freqs; i++) { - for (j = 0; freqs[j] != 0; j++) { + /* It's important to check the values in the order of @freqs, because + * that array might be sorted to contain preferred frequencies first. */ + for (j = 0; freqs[j] != 0; j++) { + for (i = 0; i < wext->num_freqs; i++) { if (wext->freqs[i] == freqs[j]) return freqs[j]; } diff --git a/src/libnm-platform/wifi/nm-wifi-utils.c b/src/libnm-platform/wifi/nm-wifi-utils.c index 8bdd65c2..08a8ec4f 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.c +++ b/src/libnm-platform/wifi/nm-wifi-utils.c @@ -60,6 +60,7 @@ _NM80211Mode nm_wifi_utils_get_mode(NMWifiUtils *data) { g_return_val_if_fail(data != NULL, _NM_802_11_MODE_UNKNOWN); + return NM_WIFI_UTILS_GET_CLASS(data)->get_mode(data); } @@ -118,6 +119,7 @@ guint32 nm_wifi_utils_get_freq(NMWifiUtils *data) { g_return_val_if_fail(data != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS(data)->get_freq(data); } @@ -126,6 +128,7 @@ nm_wifi_utils_find_freq(NMWifiUtils *data, const guint32 *freqs) { g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(freqs != NULL, 0); + return NM_WIFI_UTILS_GET_CLASS(data)->find_freq(data, freqs); } @@ -154,6 +157,28 @@ nm_wifi_utils_is_wifi(int dirfd, const char *ifname) return FALSE; } +gboolean +nm_wifi_utils_get_csme_conn_info(NMWifiUtils *data, NMPlatformCsmeConnInfo *out_conn_info) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail(data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS(data); + return klass->get_csme_conn_info ? klass->get_csme_conn_info(data, out_conn_info) : FALSE; +} + +gboolean +nm_wifi_utils_get_device_from_csme(NMWifiUtils *data) +{ + NMWifiUtilsClass *klass; + + g_return_val_if_fail(data != NULL, FALSE); + + klass = NM_WIFI_UTILS_GET_CLASS(data); + return klass->get_device_from_csme ? klass->get_device_from_csme(data) : FALSE; +} + /* OLPC Mesh-only functions */ guint32 diff --git a/src/libnm-platform/wifi/nm-wifi-utils.h b/src/libnm-platform/wifi/nm-wifi-utils.h index be33b90c..0d30c1a1 100644 --- a/src/libnm-platform/wifi/nm-wifi-utils.h +++ b/src/libnm-platform/wifi/nm-wifi-utils.h @@ -63,6 +63,12 @@ _NMSettingWirelessWakeOnWLan nm_wifi_utils_get_wake_on_wlan(NMWifiUtils *data); gboolean nm_wifi_utils_set_wake_on_wlan(NMWifiUtils *data, _NMSettingWirelessWakeOnWLan wowl); +struct _NMPlatformCsmeConnInfo; +gboolean nm_wifi_utils_get_csme_conn_info(NMWifiUtils *data, + struct _NMPlatformCsmeConnInfo *out_conn_info); + +gboolean nm_wifi_utils_get_device_from_csme(NMWifiUtils *data); + /* OLPC Mesh-only functions */ guint32 nm_wifi_utils_get_mesh_channel(NMWifiUtils *data); |