diff options
Diffstat (limited to 'src/core/devices')
| -rw-r--r-- | src/core/devices/nm-device-bond.c | 13 | ||||
| -rw-r--r-- | src/core/devices/nm-device-ethernet.c | 12 | ||||
| -rw-r--r-- | src/core/devices/nm-device-macvlan.c | 7 | ||||
| -rw-r--r-- | src/core/devices/nm-device-veth.c | 2 | ||||
| -rw-r--r-- | src/core/devices/nm-device-vxlan.c | 4 | ||||
| -rw-r--r-- | src/core/devices/nm-device.c | 671 | ||||
| -rw-r--r-- | src/core/devices/nm-device.h | 1 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-device-iwd.c | 33 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-device-wifi.c | 174 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-ap.c | 18 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-utils.c | 40 | ||||
| -rw-r--r-- | src/core/devices/wifi/nm-wifi-utils.h | 4 | ||||
| -rw-r--r-- | src/core/devices/wifi/tests/test-devices-wifi.c | 46 | ||||
| -rw-r--r-- | src/core/devices/wwan/nm-modem-manager.c | 11 |
14 files changed, 665 insertions, 371 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 39e68e96..06d41a19 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -52,11 +52,12 @@ NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \ NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_USE_CARRIER, \ NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, \ - NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX + NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY -#define OPTIONS_REAPPLY_FULL \ - OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \ - NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_NS_IP6_TARGET +#define OPTIONS_REAPPLY_FULL \ + OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \ + NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_NS_IP6_TARGET, \ + NM_SETTING_BOND_OPTION_ARP_MISSED_MAX /*****************************************************************************/ @@ -501,6 +502,8 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p props->lp_interval_has = props->lp_interval != 1; props->tlb_dynamic_lb_has = NM_IN_SET(props->mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB); props->lacp_active_has = NM_IN_SET(props->mode, NM_BOND_MODE_8023AD); + props->arp_missed_max_has = + !NM_IN_SET(props->mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB, NM_BOND_MODE_8023AD); } static void @@ -907,6 +910,8 @@ reapply_connection(NMDevice *device, NMConnection *con_old, NMConnection *con_ne set_bond_arp_ip_targets(device, s_bond); set_bond_attrs_or_default(device, s_bond, NM_MAKE_STRV(OPTIONS_REAPPLY_SUBSET)); + if (!NM_IN_SET(mode, NM_BOND_MODE_TLB, NM_BOND_MODE_ALB, NM_BOND_MODE_8023AD)) + set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX); _balance_slb_setup(self, con_new); } diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 11f691de..b550b10a 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -1684,11 +1684,13 @@ complete_connection(NMDevice *device, con_peer_name = nm_setting_veth_get_peer(s_veth); if (con_peer_name) { - nm_utils_error_set(error, - NM_UTILS_ERROR_UNKNOWN, - "mismatching veth peer \"%s\"", - con_peer_name); - return FALSE; + if (!nm_streq(con_peer_name, peer_name)) { + nm_utils_error_set(error, + NM_UTILS_ERROR_UNKNOWN, + "mismatching veth peer \"%s\"", + con_peer_name); + return FALSE; + } } else g_object_set(s_veth, NM_SETTING_VETH_PEER, peer_name, NULL); diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c index c5bcc91a..d3cbb663 100644 --- a/src/core/devices/nm-device-macvlan.c +++ b/src/core/devices/nm-device-macvlan.c @@ -468,7 +468,12 @@ static const NMDBusInterfaceInfoExtended interface_info_device_macvlan = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("NoPromisc", "b", NM_DEVICE_MACVLAN_NO_PROMISC), - NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tab", "b", NM_DEVICE_MACVLAN_TAP), ), ), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE("Tap", "b", NM_DEVICE_MACVLAN_TAP), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE( + "Tab", + "b", + NM_DEVICE_MACVLAN_TAP, + .annotations = NM_GDBUS_ANNOTATION_INFO_LIST_DEPRECATED(), ), ), ), }; static void diff --git a/src/core/devices/nm-device-veth.c b/src/core/devices/nm-device-veth.c index c4b9e234..8be29fe9 100644 --- a/src/core/devices/nm-device-veth.c +++ b/src/core/devices/nm-device-veth.c @@ -53,7 +53,7 @@ update_properties(NMDevice *device) nm_device_parent_set_ifindex(device, peer_ifindex); peer = nm_device_parent_get_device(device); - if (peer && NM_IS_DEVICE_VETH(peer) && nm_device_parent_get_ifindex(peer) <= 0) + if (peer && NM_IS_DEVICE_VETH(peer) && !nm_device_parent_get_device(peer)) update_properties(peer); } diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c index 4058287c..87844e6f 100644 --- a/src/core/devices/nm-device-vxlan.c +++ b/src/core/devices/nm-device-vxlan.c @@ -176,14 +176,14 @@ create_and_realize(NMDevice *device, if (str) { if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.local) && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.local6)) - return FALSE; + return nm_assert_unreachable_val(FALSE); } str = nm_setting_vxlan_get_remote(s_vxlan); if (str) { if (!nm_inet_parse_bin(AF_INET, str, NULL, &props.group) && !nm_inet_parse_bin(AF_INET6, str, NULL, &props.group6)) - return FALSE; + return nm_assert_unreachable_val(FALSE); } props.tos = nm_setting_vxlan_get_tos(s_vxlan); diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index a0cf1ee6..e3457e34 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -805,7 +805,7 @@ typedef struct _NMDevicePrivate { GVariant *ports_variant; /* Array of port devices D-Bus path */ char *prop_ip_iface; /* IP interface D-Bus property */ - GList *ping_operations; + CList ping_ops_lst_head; GSource *ping_timeout; } NMDevicePrivate; @@ -850,7 +850,6 @@ static const char *_activation_func_to_string(ActivationHandleFunc func); static void _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, gboolean quitting); static void queued_state_clear(NMDevice *device); -static void ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data); static void nm_device_start_ip_check(NMDevice *self); static void realize_start_setup(NMDevice *self, const NMPlatformLink *plink, @@ -907,10 +906,11 @@ static void concheck_update_state(NMDevice *self, static void sriov_op_cb(GError *error, gpointer user_data); static void device_ifindex_changed_cb(NMManager *manager, NMDevice *device_changed, NMDevice *self); -static gboolean device_link_changed(gpointer user_data); -static gboolean _get_maybe_ipv6_disabled(NMDevice *self); -static void deactivate_ready(NMDevice *self, NMDeviceStateReason reason); -static void carrier_disconnected_action_cancel(NMDevice *self); +static gboolean device_link_changed(gpointer user_data); +static gboolean _get_maybe_ipv6_disabled(NMDevice *self); +static void deactivate_ready(NMDevice *self, NMDeviceStateReason reason); +static void carrier_disconnected_action_cancel(NMDevice *self); +static const char *nm_device_get_effective_ip_config_method(NMDevice *self, int addr_family); /*****************************************************************************/ @@ -1294,7 +1294,7 @@ _prop_get_ipv6_dhcp_duid(NMDevice *self, gint64 time; guint32 timestamp; -#define EPOCH_DATETIME_THREE_YEARS (356 * 24 * 3600 * 3) +#define EPOCH_DATETIME_THREE_YEARS (365 * 24 * 3600 * 3) /* We want a variable time between the host_id timestamp and three years * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll @@ -1524,6 +1524,50 @@ _prop_get_connection_dnssec(NMDevice *self, NMConnection *connection) NM_SETTING_CONNECTION_DNSSEC_DEFAULT); } +static NMSettingIp4ConfigClat +_prop_get_ipv4_clat(NMDevice *self, gboolean do_log) +{ + NMSettingIP4Config *s_ip4 = NULL; + NMSettingIp4ConfigClat clat; + const char *method; + + s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); + if (!s_ip4) + return NM_SETTING_IP4_CONFIG_CLAT_NO; + + method = nm_device_get_effective_ip_config_method(self, AF_INET); + if (nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + return NM_SETTING_IP4_CONFIG_CLAT_NO; + + clat = nm_setting_ip4_config_get_clat(s_ip4); + if (clat == NM_SETTING_IP4_CONFIG_CLAT_DEFAULT) { + clat = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv4.clat"), + self, + NM_SETTING_IP4_CONFIG_CLAT_NO, + NM_SETTING_IP4_CONFIG_CLAT_FORCE, + NM_SETTING_IP4_CONFIG_CLAT_NO); + } + + if (clat == NM_SETTING_IP4_CONFIG_CLAT_AUTO + && !nm_streq(method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + /* clat=auto enables CLAT only with method=auto */ + clat = NM_SETTING_IP4_CONFIG_CLAT_NO; + } + + if (!HAVE_CLAT + && NM_IN_SET(clat, NM_SETTING_IP4_CONFIG_CLAT_AUTO, NM_SETTING_IP4_CONFIG_CLAT_FORCE)) { + if (do_log) { + _NMLOG(clat == NM_SETTING_IP4_CONFIG_CLAT_FORCE ? LOGL_WARN : LOGL_TRACE, + LOGD_DEVICE, + "CLAT will not work because it is disabled at build time"); + } + clat = NM_SETTING_IP4_CONFIG_CLAT_NO; + } + + return clat; +} + static NMMptcpFlags _prop_get_connection_mptcp_flags(NMDevice *self, NMConnection *connection) { @@ -1922,26 +1966,43 @@ _prop_get_ipvx_may_fail_cached(NMDevice *self, int addr_family, NMTernary *cache } static gboolean -_prop_get_ipv4_dhcp_ipv6_only_preferred(NMDevice *self) +_prop_get_ipv4_dhcp_ipv6_only_preferred(NMDevice *self, gboolean *out_is_auto) { NMSettingIP4Config *s_ip4; NMSettingIP4DhcpIpv6OnlyPreferred ipv6_only; + NM_SET_OUT(out_is_auto, FALSE); + s_ip4 = nm_device_get_applied_setting(self, NM_TYPE_SETTING_IP4_CONFIG); if (!s_ip4) return FALSE; ipv6_only = nm_setting_ip4_config_get_dhcp_ipv6_only_preferred(s_ip4); - if (ipv6_only != NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT) - return ipv6_only; + if (ipv6_only == NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_DEFAULT) { + ipv6_only = nm_config_data_get_connection_default_int64( + NM_CONFIG_GET_DATA, + NM_CON_DEFAULT("ipv4.dhcp-ipv6-only-preferred"), + self, + NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO, + NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_AUTO, + NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_AUTO); + } - return nm_config_data_get_connection_default_int64( - NM_CONFIG_GET_DATA, - NM_CON_DEFAULT("ipv4.dhcp-ipv6-only-preferred"), - self, - NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO, - NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES, - NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO); + if (NM_IN_SET(ipv6_only, + NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES, + NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_NO)) + return ipv6_only == NM_SETTING_IP4_DHCP_IPV6_ONLY_PREFERRED_YES; + + /* auto */ + NM_SET_OUT(out_is_auto, TRUE); + + if (nm_streq0(nm_device_get_effective_ip_config_method(self, AF_INET6), + NM_SETTING_IP6_CONFIG_METHOD_AUTO) + && _prop_get_ipv4_clat(self, FALSE) != NM_SETTING_IP4_CONFIG_CLAT_NO) { + return TRUE; + } + + return FALSE; } /** @@ -3623,6 +3684,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co { NML3ConfigData *l3cd; int ifindex; + gs_free char *gw_warning = NULL; nm_assert(NM_IS_DEVICE(self)); nm_assert(!connection || NM_IS_CONNECTION(connection)); @@ -3642,6 +3704,11 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co nm_l3_config_data_set_dnssec(l3cd, _prop_get_connection_dnssec(self, connection)); nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self, connection)); nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self, connection)); + + gw_warning = nm_connection_get_unreachable_gateways_warning(connection, FALSE); + if (gw_warning) + _LOGW(LOGD_IP, "%s", gw_warning); + return l3cd; } @@ -4924,7 +4991,7 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N if (state >= NM_DEVICE_STATE_IP_CONFIG && state < NM_DEVICE_STATE_DEACTIVATING) { /* FIXME(l3cfg): MTU handling should be moved to l3cfg. */ if (l3cd) - priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu(l3cd); + priv->ip6_mtu = nm_l3_config_data_get_ip6_mtu_ra(l3cd); _commit_mtu(self); } _dev_ipll4_check_fallback(self, l3cd); @@ -6345,6 +6412,14 @@ concheck_is_possible(NMDevice *self) if (priv->state == NM_DEVICE_STATE_UNKNOWN) return FALSE; + if (!nm_config_data_get_device_config_boolean_by_device( + NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_CHECK_CONNECTIVITY, + self, + TRUE, + TRUE)) + return FALSE; + return TRUE; } @@ -6365,8 +6440,10 @@ concheck_periodic_schedule_do(NMDevice *self, int addr_family, gint64 now_ns) goto out; } - if (!concheck_is_possible(self)) + if (!concheck_is_possible(self)) { + concheck_update_state(self, addr_family, NM_CONNECTIVITY_UNKNOWN, FALSE); goto out; + } nm_assert(now_ns > 0); nm_assert(priv->concheck_x[IS_IPv4].p_cur_interval > 0); @@ -6589,7 +6666,11 @@ concheck_update_interval(NMDevice *self, int addr_family, gboolean check_now) concheck_periodic_schedule_do(self, addr_family, 0); /* also update the fake connectivity state. */ - concheck_update_state(self, addr_family, NM_CONNECTIVITY_FAKE, TRUE); + if (concheck_is_possible(self)) + concheck_update_state(self, addr_family, NM_CONNECTIVITY_FAKE, TRUE); + else + concheck_update_state(self, addr_family, NM_CONNECTIVITY_UNKNOWN, FALSE); + return; } @@ -6618,6 +6699,7 @@ concheck_update_state(NMDevice *self, /* @state is a result of the connectivity check. We only expect a precise * number of possible values. */ nm_assert(NM_IN_SET(state, + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_LIMITED, NM_CONNECTIVITY_PORTAL, NM_CONNECTIVITY_FULL, @@ -6941,8 +7023,11 @@ nm_device_check_connectivity(NMDevice *self, NMDeviceConnectivityCallback callback, gpointer user_data) { - if (!concheck_is_possible(self)) + if (!concheck_is_possible(self)) { + concheck_update_state(self, AF_INET, NM_CONNECTIVITY_UNKNOWN, FALSE); + concheck_update_state(self, AF_INET6, NM_CONNECTIVITY_UNKNOWN, FALSE); return NULL; + } concheck_periodic_schedule_set(self, addr_family, CONCHECK_SCHEDULE_CHECK_EXTERNAL); return concheck_start(self, addr_family, callback, user_data, FALSE); @@ -7149,7 +7234,9 @@ nm_device_controller_release_port(NMDevice *self, info = find_port_info(self, port); - if (info->port_state == PORT_STATE_ATTACHED) + if (!info) + port_state_str = "(not registered)"; + else if (info->port_state == PORT_STATE_ATTACHED) port_state_str = "(attached)"; else if (info->port_state == PORT_STATE_NOT_ATTACHED) port_state_str = "(not attached)"; @@ -7162,7 +7249,7 @@ nm_device_controller_release_port(NMDevice *self, "controller: release one port " NM_HASH_OBFUSCATE_PTR_FMT "/%s %s%s", NM_HASH_OBFUSCATE_PTR(port), nm_device_get_iface(port), - !info ? "(not registered)" : port_state_str, + port_state_str, release_type == RELEASE_PORT_TYPE_CONFIG_FORCE ? " (force-configure)" : (release_type == RELEASE_PORT_TYPE_CONFIG ? " (configure)" : "(no-config)")); @@ -7191,6 +7278,8 @@ nm_device_controller_release_port(NMDevice *self, if (ret == NM_TERNARY_DEFAULT) { port_priv->port_detach_count++; port_priv->port_detach_reason = reason; + } else { + g_object_unref(port); } } @@ -7798,6 +7887,12 @@ device_link_changed(gpointer user_data) NM_UNMANAGED_PLATFORM_INIT, NM_UNMAN_FLAG_OP_SET_MANAGED, nm_device_get_manage_reason_external(self)); + + /* Now that we got UDEV's announcement we need to check ignore-carrier again. + * This is because the permanent MAC might have been set or changed. If we don't + * recheck we would ignore rules matching by MAC address. */ + priv->ignore_carrier = + nm_config_data_get_ignore_carrier_by_device(nm_config_get_data(nm_config_get()), self); } _dev_unmanaged_check_external_down(self, FALSE, FALSE); @@ -8330,6 +8425,17 @@ config_changed(NMConfig *config, && !nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV)) device_init_static_sriov_num_vfs(self); } + + if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_VALUES) && concheck_is_possible(self)) { + /* restart (periodic) connectivity checks if they were previously disabled */ + if (!nm_config_data_get_device_config_boolean_by_device( + old_data, + NM_CONFIG_KEYFILE_KEY_DEVICE_CHECK_CONNECTIVITY, + self, + TRUE, + TRUE)) + nm_device_check_connectivity_update_interval(self); + } } static void @@ -8463,10 +8569,13 @@ realize_start_setup(NMDevice *self, nm_device_update_initial_hw_address(self); nm_device_update_permanent_hw_address(self, FALSE); - /* Note: initial hardware address must be read before calling get_ignore_carrier() */ + /* Note: initial hardware address must be read before calling get_ignore_carrier(). We'll + * need to recheck ignore_carrier again after UDEV's announcement, as the permanent MAC + * address may be set by UDEV. */ config = nm_config_get(); priv->ignore_carrier = nm_config_data_get_ignore_carrier_by_device(nm_config_get_data(config), self); + if (!priv->config_changed_id) { priv->config_changed_id = g_signal_connect(config, NM_CONFIG_SIGNAL_CONFIG_CHANGED, @@ -11440,6 +11549,8 @@ _dev_ipmanual_start(NMDevice *self) if (_prop_get_ipvx_routed_dns(self, AF_INET6) == NM_SETTING_IP_CONFIG_ROUTED_DNS_YES) { nm_l3_config_data_set_routed_dns(l3cd, AF_INET6, TRUE); } + + nm_l3_config_data_set_clat_config(l3cd, _prop_get_ipv4_clat(self, TRUE)); } if (!l3cd) { @@ -11748,8 +11859,9 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) gboolean hostname_is_fqdn; gboolean send_client_id; guint8 dscp; - gboolean dscp_explicit = FALSE; - gboolean ipv6_only_pref = FALSE; + gboolean dscp_explicit = FALSE; + gboolean ipv6_only_pref = FALSE; + gboolean ipv6_only_pref_auto = FALSE; client_id = _prop_get_ipv4_dhcp_client_id(self, connection, hwaddr, &send_client_id); dscp = _prop_get_ipv4_dhcp_dscp(self, &dscp_explicit); @@ -11768,13 +11880,15 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip); } - if (_prop_get_ipv4_dhcp_ipv6_only_preferred(self)) { + if (_prop_get_ipv4_dhcp_ipv6_only_preferred(self, &ipv6_only_pref_auto)) { if (nm_streq0(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED)) { _LOGI_ipdhcp( addr_family, "not requesting the \"IPv6-only preferred\" option because IPv6 is disabled"); } else { - _LOGD_ipdhcp(addr_family, "requesting the \"IPv6-only preferred\" option"); + _LOGD_ipdhcp(addr_family, + "requesting the \"IPv6-only preferred\" option (%s enabled)", + ipv6_only_pref_auto ? "automatically" : "explicitly"); ipv6_only_pref = TRUE; } } @@ -13585,13 +13699,21 @@ activate_stage3_ip_config(NMDevice *self) nm_device_get_ip_iface(self)); } - /* We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG above. - * Note that kernel changes the MTU of bond ports, so we want to commit the MTU - * afterwards! + /* + * Let's make sure MTU matches what is configured. The reason it's done at this + * precise location is twofold: + * + * (1) Attaching ports above might affect the MTU. + * + * We currently will attach ports in the state change NM_DEVICE_STATE_IP_CONFIG + * above. This might reset the MTU to something different from the bond controller + * and it might not be a working configuration. But it's what the user asked for. + * + * (2) When MTU is under 1280 IPv6 can not work. * - * This might reset the MTU to something different from the bond controller and - * it might not be a working configuration. But it's what the user asked for, so - * let's do it! */ + * Kernel will not expose sysctls, create or accept addresses that are needed for IPv6 + * configuration when the MTU is too small (under 1280). + */ _commit_mtu(self); if (!nm_device_managed_type_is_external(self) @@ -13600,12 +13722,6 @@ activate_stage3_ip_config(NMDevice *self) && !NM_IN_STRSET(priv->ipv6_method, NM_SETTING_IP6_CONFIG_METHOD_DISABLED, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { - /* Ensure the MTU makes sense. If it was below 1280 the kernel would not - * expose any ipv6 sysctls or allow presence of any addresses on the interface, - * including LL, which * would make it impossible to autoconfigure MTU to a - * correct value. */ - _commit_mtu(self); - /* Any method past this point requires an IPv6LL address. Use NM-controlled * IPv6LL if this is not an assumed connection, since assumed connections * will already have IPv6 set up. @@ -14583,6 +14699,12 @@ check_and_reapply_connection(NMDevice *self, reactivate_proxy_config(self); + /* Reapply may have changed the per-device L3 merge flags (ignore-auto-dns, + * ignore-auto-routes, never-default). Re-register the active l3cds so the + * refreshed flags reach l3cfg even for sources that are not restarted on + * reapply (e.g. DHCPv6 when the NDisc DHCP level is unchanged). */ + _dev_l3_register_l3cds(self, priv->l3cfg, TRUE, FALSE); + nm_device_l3cfg_commit( self, NM_FLAGS_HAS(reapply_flags, NM_DEVICE_REAPPLY_FLAGS_PRESERVE_EXTERNAL_IP) @@ -15588,35 +15710,15 @@ _dispatcher_complete_proceed_state(NMDispatcherCallId *call_id, gpointer user_da /*****************************************************************************/ typedef struct { - NMLogDomain log_domain; - NMDevice *device; - gboolean ping_addresses_require_all; - GSource *watch; - GPid pid; - char *binary; - char *address; - guint deadline; -} PingOperation; - -static PingOperation * -ping_operation_new(NMDevice *self, - NMLogDomain log_domain, - const char *address, - const char *ping_binary, - guint ping_timeout, - gboolean ip_ping_addresses_require_all) -{ - PingOperation *ping_op = g_new0(PingOperation, 1); - - ping_op->device = self; - ping_op->log_domain = log_domain; - ping_op->address = g_strdup(address); - ping_op->binary = g_strdup(ping_binary); - ping_op->deadline = ping_timeout + 10; - ping_op->ping_addresses_require_all = ip_ping_addresses_require_all; + char *addr_str; + NMIPAddrTyped addr_bin; - return ping_op; -} + NMLogDomain log_domain; + NMDevice *device; + GCancellable *cancellable; + gboolean require_all; + CList ping_ops_lst; +} PingOperation; static void ip_check_pre_up(NMDevice *self) @@ -15640,188 +15742,154 @@ ip_check_pre_up(NMDevice *self) } static void -cleanup_ping_operation(PingOperation *ping_op) +ping_op_cleanup(PingOperation *ping_op) { - if (ping_op->watch) { - nm_clear_g_source_inst(&ping_op->watch); - } - - if (ping_op->pid) { - nm_utils_kill_child_async(ping_op->pid, - SIGTERM, - ping_op->log_domain, - "ping", - 1000, - NULL, - NULL); - ping_op->pid = 0; - } - - nm_clear_g_free(&ping_op->binary); - nm_clear_g_free(&ping_op->address); + nm_clear_g_cancellable(&ping_op->cancellable); + nm_clear_g_free(&ping_op->addr_str); + c_list_unlink_stale(&ping_op->ping_ops_lst); g_free(ping_op); } -static gboolean -spawn_ping_for_operation(NMDevice *self, PingOperation *ping_op) -{ - gs_free char *str_timeout = NULL; - gs_free char *tmp_str = NULL; - const char *args[] = {ping_op->binary, - "-I", - nm_device_get_ip_iface(self), - "-c", - "1", - "-w", - NULL, - ping_op->address, - NULL}; - gs_free_error GError *error = NULL; - gboolean ret; - - args[6] = str_timeout = g_strdup_printf("%u", ping_op->deadline); - - tmp_str = g_strjoinv(" ", (char **) args); - _LOGD(ping_op->log_domain, "ping: running '%s'", tmp_str); - - ret = g_spawn_async("/", - (char **) args, - NULL, - G_SPAWN_DO_NOT_REAP_CHILD, - NULL, - NULL, - &ping_op->pid, - &error); +static void +ping_cleanup(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + PingOperation *ping_op; - if (ret) { - ping_op->watch = nm_g_child_watch_add_source(ping_op->pid, ip_check_ping_watch_cb, ping_op); - } else { - _LOGD(ping_op->log_domain, "ping: could not spawn %s: %s", ping_op->binary, error->message); + while ((ping_op = c_list_first_entry(&priv->ping_ops_lst_head, PingOperation, ping_ops_lst))) { + ping_op_cleanup(ping_op); } - return ret; + nm_clear_g_source_inst(&priv->ping_timeout); } -static gboolean -respawn_ping_cb(gpointer user_data) +static void +ping_host_cb(GObject *source, GAsyncResult *result, gpointer user_data) { - PingOperation *ping_op = (PingOperation *) user_data; - NMDevice *self = ping_op->device; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMDevice *self; + NMDevicePrivate *priv; + PingOperation *ping_op = user_data; + gs_free_error GError *error = NULL; + gboolean success; + NMLogDomain log_domain; + gs_free char *addr_str = NULL; + + success = nm_utils_ping_host_finish(result, &error); + if (nm_utils_error_is_cancelled(error)) + return; - nm_clear_g_source_inst(&ping_op->watch); + self = NM_DEVICE(ping_op->device); + priv = NM_DEVICE_GET_PRIVATE(self); + log_domain = ping_op->log_domain; + addr_str = g_steal_pointer(&ping_op->addr_str); - if (!spawn_ping_for_operation(self, ping_op)) { - priv->ping_operations = g_list_remove(priv->ping_operations, ping_op); - cleanup_ping_operation(ping_op); + if (!success) { + /* it should never fail because we set an infinite timeout */ + nm_assert_not_reached(); + return; + } - if (g_list_length(priv->ping_operations) == 0) { - ip_check_pre_up(self); + if (ping_op->require_all) { + ping_op_cleanup(ping_op); + if (!c_list_is_empty(&priv->ping_ops_lst_head)) { + _LOGD(log_domain, + "ping: check on address %s succeeded, waiting for other addresses", + addr_str); + return; } } - return FALSE; + _LOGD(log_domain, "ping: check on address %s succeeded, continuing the activation", addr_str); + + ping_cleanup(self); + ip_check_pre_up(self); } static void -ip_check_ping_watch_cb(GPid pid, int status, gpointer user_data) +ping_operation_start(NMDevice *self, + const char *addr_str, + NMIPAddrTyped *addr_bin, + gboolean require_all) { - PingOperation *ping_op = (PingOperation *) user_data; - NMDevice *self = ping_op->device; - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - gboolean success = FALSE; - - if (!ping_op->watch) - return; - - nm_clear_g_source_inst(&ping_op->watch); - ping_op->pid = 0; - - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) == 0) { - _LOGD(ping_op->log_domain, "ping: ping succeeded on %s", ping_op->address); - success = TRUE; - } else { - _LOGD(ping_op->log_domain, - "ping: ping failed with error code %d on %s", - WEXITSTATUS(status), - ping_op->address); - } + PingOperation *ping_op; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMIPAddrTyped addr_bin_local; + char buf[NM_INET_ADDRSTRLEN]; + int addr_family; + int ret; + + /* Exactly one of the two must be set */ + nm_assert(!!addr_str ^ !!addr_bin); + + /* Derive the string address from the binary and vice versa */ + if (addr_str) { + ret = nm_inet_parse_bin_full(AF_UNSPEC, + FALSE, + addr_str, + &addr_family, + &addr_bin_local.addr.addr_ptr); + nm_assert(ret); + addr_bin_local.addr_family = addr_family; + addr_bin = &addr_bin_local; } else { - _LOGD(ping_op->log_domain, - "ping: stopped unexpectedly with status %d on %s", - status, - ping_op->address); - } - - if (success) { - if (ping_op->ping_addresses_require_all) { - priv->ping_operations = g_list_remove(priv->ping_operations, ping_op); - if (g_list_length(priv->ping_operations) == 0) { - _LOGD(ping_op->log_domain, - "ping: ip-ping-addresses requires all, all ping checks on ip-ping-addresses " - "succeeded"); - if (priv->ping_timeout) - nm_clear_g_source_inst(&priv->ping_timeout); - ip_check_pre_up(self); - } - cleanup_ping_operation(ping_op); - } else { - nm_assert(priv->ping_operations); - - g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation); - priv->ping_operations = NULL; + nm_inet_ntop(addr_bin->addr_family, addr_bin->addr.addr_ptr, buf); + addr_str = buf; + } + + /* When pinging the gateway, the caller must ensure that the IP configuration is ready. + * For the ip-ping-addresses property, a valid connection always has may-fail=no for + * the families of all the target addresses. Thus, at this point the IP configuration + * must also be ready. */ + nm_assert(priv->ip_data_x[NM_IS_IPv4(addr_bin->addr_family)].state == NM_DEVICE_IP_STATE_READY); + + ping_op = g_new(PingOperation, 1); + *ping_op = (PingOperation) { + .device = self, + .cancellable = g_cancellable_new(), + .require_all = require_all, + .addr_bin = *addr_bin, + .addr_str = g_strdup(addr_str), + .log_domain = (addr_bin->addr_family == AF_INET) ? LOGD_IP4 : LOGD_IP6, + }; - if (priv->ping_timeout) - nm_clear_g_source_inst(&priv->ping_timeout); + /* Start the asynchronous ping operation */ + nm_utils_ping_host(ping_op->addr_bin, + nm_device_get_ip_ifindex(self), + 0, /* try forever */ + ping_op->cancellable, + ping_host_cb, + ping_op); - _LOGD(ping_op->log_domain, - "ping: ip-ping-addresses requires any, one ping check on ip-ping-addresses " - "succeeded"); - ip_check_pre_up(self); - } - } else { - /* If ping exited with an error it may have returned early, - * wait 1 second and restart it */ - ping_op->watch = nm_g_timeout_add_seconds_source(1, respawn_ping_cb, ping_op); - } + c_list_link_tail(&priv->ping_ops_lst_head, &ping_op->ping_ops_lst); } static gboolean ip_check_ping_timeout_cb(gpointer user_data) { - NMDevice *self = NM_DEVICE(user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - _LOGW(LOGD_DEVICE, "ping timeout: unreachable gateway or ip-ping-addresses"); - - if (priv->ping_operations) { - g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation); - priv->ping_operations = NULL; + NMDevice *self = NM_DEVICE(user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + PingOperation *ping_op; + nm_auto_free_gstring GString *str = NULL; + + if (_LOGW_ENABLED(LOGD_DEVICE)) { + str = g_string_new(""); + c_list_for_each_entry (ping_op, &priv->ping_ops_lst_head, ping_ops_lst) { + if (str->len != 0) + g_string_append(str, ", "); + g_string_append(str, ping_op->addr_str); + } + _LOGW(LOGD_DEVICE, + "ping: the following addresses were not reachable within the timeout: %s", + str->str); } - if (priv->ping_timeout) - nm_clear_g_source_inst(&priv->ping_timeout); + ping_cleanup(self); ip_check_pre_up(self); return FALSE; } -static gboolean -start_ping(NMDevice *self, PingOperation *ping_op) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - - if (spawn_ping_for_operation(self, ping_op)) { - priv->ping_operations = g_list_append(priv->ping_operations, ping_op); - return TRUE; - } - - cleanup_ping_operation(ping_op); - return FALSE; -} - static void nm_device_start_ip_check(NMDevice *self) { @@ -15830,17 +15898,13 @@ nm_device_start_ip_check(NMDevice *self) NMSettingConnection *s_con; guint gw_ping_timeout = 0; guint ip_ping_timeout = 0; - const char *ping_binary = NULL; - char buf[NM_INET_ADDRSTRLEN]; - NMLogDomain log_domain = LOGD_IP4; - gboolean ip_ping_addresses_require_all; - gboolean ping_started = FALSE; + gboolean require_all; + NMIPAddrTyped addr_bin = {}; /* Shouldn't be any active ping here, since IP_CHECK happens after the * first IP method completes. Any subsequently completing IP method doesn't - * get checked. - */ - g_return_if_fail(priv->ping_operations == NULL); + * get checked. */ + g_return_if_fail(c_list_is_empty(&priv->ping_ops_lst_head)); g_return_if_fail(priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY || priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY); @@ -15849,100 +15913,71 @@ nm_device_start_ip_check(NMDevice *self) s_con = nm_connection_get_setting_connection(connection); g_assert(s_con); - gw_ping_timeout = nm_setting_connection_get_gateway_ping_timeout(s_con); - ip_ping_addresses_require_all = _prop_get_connection_ip_ping_addresses_require_all(self, s_con); - ip_ping_timeout = nm_setting_connection_get_ip_ping_timeout(s_con); + gw_ping_timeout = nm_setting_connection_get_gateway_ping_timeout(s_con); + ip_ping_timeout = nm_setting_connection_get_ip_ping_timeout(s_con); + require_all = _prop_get_connection_ip_ping_addresses_require_all(self, s_con); - buf[0] = '\0'; - if (gw_ping_timeout != 0 && ip_ping_timeout == 0) { + /* the timeouts are mutually exclusive */ + nm_assert(gw_ping_timeout == 0 || ip_ping_timeout == 0); + + if (gw_ping_timeout > 0) { const NMPObject *gw; const NML3ConfigData *l3cd; - _LOGD(LOGD_DEVICE, "starting ping gateway..."); - l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL; if (!l3cd) { /* pass */ } else if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY) { gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET); if (gw) { - nm_inet4_ntop(NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway, buf); - ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL); - log_domain = LOGD_IP4; + addr_bin.addr_family = AF_INET; + addr_bin.addr.addr4 = NMP_OBJECT_CAST_IP4_ROUTE(gw)->gateway; } } else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY) { gw = nm_l3_config_data_get_best_default_route(l3cd, AF_INET6); if (gw) { - nm_inet6_ntop(&NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway, buf); - ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL); - log_domain = LOGD_IP6; + addr_bin.addr_family = AF_INET6; + addr_bin.addr.addr6 = NMP_OBJECT_CAST_IP6_ROUTE(gw)->gateway; } } - } - - if (buf[0]) { - PingOperation *ping_op = ping_operation_new(self, - log_domain, - buf, - ping_binary, - gw_ping_timeout, - ip_ping_addresses_require_all); - - if (start_ping(self, ping_op)) - ping_started = TRUE; - } - if (gw_ping_timeout == 0 && ip_ping_timeout != 0) { + if (addr_bin.addr_family != AF_UNSPEC) { + _LOGD(LOGD_DEVICE, + "starting ping on the IPv%c gateway with a %u seconds timeout", + nm_utils_addr_family_to_char(addr_bin.addr_family), + gw_ping_timeout); + ping_operation_start(self, NULL, &addr_bin, require_all); + } + } else if (ip_ping_timeout > 0) { const NML3ConfigData *l3cd; + GArray *ip_ping_addresses; + const char *const *strv; guint i; - GArray *ip_ping_addresses = _nm_setting_connection_get_ip_ping_addresses(s_con); - const char *const *strv = nm_strvarray_get_strv_notempty(ip_ping_addresses, NULL); - _LOGD(LOGD_DEVICE, "starting ping ip addresses..."); + ip_ping_addresses = _nm_setting_connection_get_ip_ping_addresses(s_con); + strv = nm_strvarray_get_strv_notnull(ip_ping_addresses, NULL); - l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL; + _LOGD(LOGD_DEVICE, + "starting ping on the ip-ping-addresses with a %u seconds timeout", + ip_ping_timeout); + l3cd = priv->l3cfg ? nm_l3cfg_get_combined_l3cd(priv->l3cfg, TRUE) : NULL; if (l3cd) { for (i = 0; strv[i]; i++) { - const char *s = strv[i]; - struct in_addr ipv4_addr; - struct in6_addr ipv6_addr; - - if (priv->ip_data_4.state == NM_DEVICE_IP_STATE_READY - && inet_pton(AF_INET, (const char *) s, &ipv4_addr)) { - ping_binary = nm_utils_find_helper("ping", "/usr/bin/ping", NULL); - log_domain = LOGD_IP4; - } else if (priv->ip_data_6.state == NM_DEVICE_IP_STATE_READY - && inet_pton(AF_INET6, (const char *) s, &ipv6_addr)) { - ping_binary = nm_utils_find_helper("ping6", "/usr/bin/ping6", NULL); - log_domain = LOGD_IP6; - } else - continue; - - if (s[0]) { - PingOperation *ping_op = ping_operation_new(self, - log_domain, - s, - ping_binary, - ip_ping_timeout, - ip_ping_addresses_require_all); - - if (start_ping(self, ping_op)) - ping_started = TRUE; - } + ping_operation_start(self, strv[i], NULL, require_all); } } } - if (ping_started) { + if (c_list_is_empty(&priv->ping_ops_lst_head)) { + /* No ping operation in progress, advance to pre-up */ + ip_check_pre_up(self); + } else { priv->ping_timeout = - nm_g_timeout_add_seconds_source(gw_ping_timeout ? gw_ping_timeout : ip_ping_timeout, + nm_g_timeout_add_seconds_source(ip_ping_timeout > 0 ? ip_ping_timeout : gw_ping_timeout, ip_check_ping_timeout_cb, self); } - /* If no ping was started, just advance to pre_up. */ - else - ip_check_pre_up(self); } /*****************************************************************************/ @@ -17401,17 +17436,11 @@ _cancel_activation(NMDevice *self) _dispatcher_cleanup(self); - if (priv->ping_operations) { - g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation); - priv->ping_operations = NULL; - } - - if (priv->ping_timeout) - nm_clear_g_source_inst(&priv->ping_timeout); - _dev_ip_state_cleanup(self, AF_INET, FALSE); _dev_ip_state_cleanup(self, AF_INET6, FALSE); + ping_cleanup(self); + /* Break the activation chain */ activation_source_clear(self); } @@ -17968,6 +17997,14 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); } break; + case NM_DEVICE_STATE_DEACTIVATING: + /* When deactivating, certain devices are removed/disconnected after the + * STATE_CHANGED signal is sent and before the DHCP release packet + * can be sent. To ensure the release packet is sent, we cleanup DHCP + * before the signal is emitted*/ + _dev_ipdhcpx_cleanup(self, AF_INET, TRUE, FALSE); + _dev_ipdhcpx_cleanup(self, AF_INET6, TRUE, FALSE); + break; case NM_DEVICE_STATE_DISCONNECTED: if (old_state > NM_DEVICE_STATE_DISCONNECTED) { /* Ensure devices that previously assumed a connection now have @@ -18017,6 +18054,7 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, (guint32) state, (guint32) old_state, (guint32) reason); + g_signal_emit(self, signals[STATE_CHANGED], 0, @@ -18167,13 +18205,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, break; } case NM_DEVICE_STATE_SECONDARIES: - if (priv->ping_operations) { - g_list_free_full(priv->ping_operations, (GDestroyNotify) cleanup_ping_operation); - priv->ping_operations = NULL; - } - if (priv->ping_timeout) - nm_clear_g_source_inst(&priv->ping_timeout); _LOGD(LOGD_DEVICE, "device entered SECONDARIES state"); + ping_cleanup(self); break; default: break; @@ -19153,14 +19186,14 @@ nm_device_get_supplicant_timeout(NMDevice *self) SUPPLICANT_DEFAULT_TIMEOUT); } -gboolean -nm_device_auth_retries_try_next(NMDevice *self) +static int +_device_get_auth_retries(NMDevice *self) { NMDevicePrivate *priv; NMSettingConnection *s_con; int auth_retries; - g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); + g_return_val_if_fail(NM_IS_DEVICE(self), 0); priv = NM_DEVICE_GET_PRIVATE(self); auth_retries = priv->auth_retries; @@ -19192,13 +19225,47 @@ nm_device_auth_retries_try_next(NMDevice *self) priv->auth_retries = auth_retries; } + return auth_retries; +} + +gboolean +nm_device_auth_retries_has_next(NMDevice *self) +{ + int auth_retries; + + g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); + + auth_retries = _device_get_auth_retries(self); + + if (auth_retries == NM_DEVICE_AUTH_RETRIES_INFINITY) + return TRUE; + + if (auth_retries > 0) + return TRUE; + + return FALSE; +} + +gboolean +nm_device_auth_retries_try_next(NMDevice *self) +{ + NMDevicePrivate *priv; + int auth_retries; + + g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); + + priv = NM_DEVICE_GET_PRIVATE(self); + auth_retries = _device_get_auth_retries(self); + if (auth_retries == NM_DEVICE_AUTH_RETRIES_INFINITY) return TRUE; if (auth_retries <= 0) { nm_assert(auth_retries == 0); return FALSE; } + priv->auth_retries--; + return TRUE; } @@ -19838,6 +19905,8 @@ nm_device_init(NMDevice *self) priv->available_connections = g_hash_table_new_full(nm_direct_hash, NULL, g_object_unref, NULL); priv->ip6_saved_properties = g_hash_table_new_full(nm_str_hash, g_str_equal, NULL, g_free); + c_list_init(&priv->ping_ops_lst_head); + priv->managed_type_ = NM_DEVICE_MANAGED_TYPE_EXTERNAL; /* If networking is already disabled at boot, we want to manage all devices * after re-enabling networking; hence, the initial state is MANAGED. */ diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index 2f287953..c8069d7c 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -791,6 +791,7 @@ void nm_device_update_permanent_hw_address(NMDevice *self, gboolean force_fr void nm_device_update_dynamic_ip_setup(NMDevice *self, const char *reason); guint nm_device_get_supplicant_timeout(NMDevice *self); +gboolean nm_device_auth_retries_has_next(NMDevice *self); gboolean nm_device_auth_retries_try_next(NMDevice *self); gboolean nm_device_hw_addr_get_cloned(NMDevice *self, diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index fa6e2f9d..94b9a7d7 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -2270,6 +2270,37 @@ add_new: return NM_ACT_STAGE_RETURN_SUCCESS; } +static void +set_powersave(NMDevice *device) +{ + NMDeviceIwd *self = NM_DEVICE_IWD(device); + NMSettingWireless *s_wireless; + NMSettingWirelessPowersave val; + + s_wireless = nm_device_get_applied_setting(device, NM_TYPE_SETTING_WIRELESS); + + g_return_if_fail(s_wireless); + + val = nm_setting_wireless_get_powersave(s_wireless); + if (val == NM_SETTING_WIRELESS_POWERSAVE_DEFAULT) { + val = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA, + "wifi.powersave", + device, + NM_SETTING_WIRELESS_POWERSAVE_IGNORE, + NM_SETTING_WIRELESS_POWERSAVE_ENABLE, + NM_SETTING_WIRELESS_POWERSAVE_IGNORE); + } + + _LOGT(LOGD_WIFI, "powersave is set to %u", (unsigned) val); + + if (val == NM_SETTING_WIRELESS_POWERSAVE_IGNORE) + return; + + nm_platform_wifi_set_powersave(nm_device_get_platform(device), + nm_device_get_ifindex(device), + val == NM_SETTING_WIRELESS_POWERSAVE_ENABLE); +} + static NMActStageReturn act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) { @@ -2297,6 +2328,8 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) goto out_fail; } + set_powersave(device); + /* With priv->iwd_autoconnect we have to let IWD handle retries for * infrastructure networks. IWD will not necessarily retry the same * network after a failure but it will likely go into an autoconnect diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index b41ed5e1..b836c1e8 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -194,6 +194,9 @@ static void supplicant_iface_notify_p2p_available(NMSupplicantInterface *iface, static void supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self); +static void supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface, + NMDeviceWifi *self); + static void periodic_update(NMDeviceWifi *self); static void ap_add_remove(NMDeviceWifi *self, @@ -631,6 +634,10 @@ supplicant_interface_acquire_cb(NMSupplicantManager *supplicant_manager, NM_SUPPLICANT_INTERFACE_PSK_MISMATCH, G_CALLBACK(supplicant_iface_notify_wpa_psk_mismatch_cb), self); + g_signal_connect(priv->sup_iface, + NM_SUPPLICANT_INTERFACE_SAE_MISMATCH, + G_CALLBACK(supplicant_iface_notify_wpa_sae_mismatch_cb), + self); _scan_notify_is_scanning(self); @@ -2191,21 +2198,16 @@ supplicant_iface_wps_credentials_cb(NMSupplicantInterface *iface, val_key = g_variant_lookup_value(credentials, "Key", G_VARIANT_TYPE_BYTESTRING); if (val_key) { - char psk[64]; + char psk[65]; array = g_variant_get_fixed_array(val_key, &psk_len, 1); - if (psk_len >= 8 && psk_len <= 63) { - memcpy(psk, array, psk_len); - psk[psk_len] = '\0'; - if (g_utf8_validate(psk, psk_len, NULL)) { - secrets = g_variant_new_parsed("[{%s, [{%s, <%s>}]}]", - NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, - NM_SETTING_WIRELESS_SECURITY_PSK, - psk); - g_variant_ref_sink(secrets); - } - } - if (!secrets) + if (nm_wifi_utils_wps_key_to_psk((const guint8 *) array, psk_len, &psk)) { + secrets = g_variant_new_parsed("[{%s, [{%s, <%s>}]}]", + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SETTING_WIRELESS_SECURITY_PSK, + psk); + g_variant_ref_sink(secrets); + } else _LOGW(LOGD_DEVICE | LOGD_WIFI, "WPS: ignore invalid PSK"); } @@ -2244,6 +2246,26 @@ wps_timeout_cb(gpointer user_data) return G_SOURCE_REMOVE; } +static gboolean +wifi_connection_is_new(NMDeviceWifi *self) +{ + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + NMSettingsConnection *connection; + guint64 timestamp = 0; + + req = nm_device_get_act_request(device); + g_return_val_if_fail(NM_IS_ACT_REQUEST(req), TRUE); + + connection = nm_act_request_get_settings_connection(req); + g_return_val_if_fail(NM_IS_SETTINGS_CONNECTION(connection), TRUE); + + if (nm_settings_connection_get_timestamp(connection, ×tamp) && timestamp != 0) + return FALSE; + + return TRUE; +} + static void wifi_secrets_get_secrets(NMDeviceWifi *self, const char *setting_name, @@ -2398,10 +2420,11 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, NMSupplicantInterfaceState old_state, int disconnect_reason) { - NMDevice *device = NM_DEVICE(self); - NMActRequest *req; - const char *setting_name = NULL; - gboolean handled = FALSE; + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + const char *setting_name = NULL; + NMSecretAgentGetSecretsFlags secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; g_return_val_if_fail(new_state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED, FALSE); @@ -2411,8 +2434,7 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, req = nm_device_get_act_request(NM_DEVICE(self)); g_return_val_if_fail(req != NULL, FALSE); - if (need_new_8021x_secrets(self, old_state, &setting_name) - || need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + if (need_new_8021x_secrets(self, old_state, &setting_name)) { nm_act_request_clear_secrets(req); _LOGI(LOGD_DEVICE | LOGD_WIFI, @@ -2422,14 +2444,54 @@ handle_8021x_or_psk_auth_fail(NMDeviceWifi *self, nm_device_state_changed(device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - wifi_secrets_get_secrets(self, - setting_name, - NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION - | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); - handled = TRUE; + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; + } + + if (need_new_wpa_psk(self, old_state, disconnect_reason, &setting_name)) { + nm_act_request_clear_secrets(req); + cleanup_association_attempt(self, TRUE); + + if (wifi_connection_is_new(self)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) new connection disconnected during association, asking for " + "new key"); + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + return TRUE; + } + + if (!nm_device_auth_retries_try_next(device)) { + nm_device_state_changed(device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + return TRUE; + } + + if (nm_device_auth_retries_has_next(device)) { + secret_flags &= ~NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + _LOGI( + LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, reauthenticating connection"); + } else { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, asking for new key"); + } + + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, setting_name, secret_flags); + + return TRUE; } - return handled; + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) disconnected during association, retrying connection"); + + return FALSE; } static gboolean @@ -2861,6 +2923,12 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) return; + if (!wifi_connection_is_new(self) && nm_device_auth_retries_has_next(device)) { + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) psk mismatch reported by supplicant, retrying connection"); + return; + } + _LOGI(LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) psk mismatch reported by supplicant, asking for new key"); @@ -2879,6 +2947,34 @@ supplicant_iface_notify_wpa_psk_mismatch_cb(NMSupplicantInterface *iface, NMDevi | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); } +static void +supplicant_iface_notify_wpa_sae_mismatch_cb(NMSupplicantInterface *iface, NMDeviceWifi *self) +{ + NMDevice *device = NM_DEVICE(self); + NMActRequest *req; + const char *setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME; + + if (nm_device_get_state(device) != NM_DEVICE_STATE_CONFIG) + return; + + _LOGI(LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) SAE password mismatch reported by supplicant, asking for new key"); + + req = nm_device_get_act_request(NM_DEVICE(self)); + g_return_if_fail(req != NULL); + + nm_act_request_clear_secrets(req); + + cleanup_association_attempt(self, TRUE); + nm_device_state_changed(device, + NM_DEVICE_STATE_NEED_AUTH, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + wifi_secrets_get_secrets(self, + setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW); +} + /* * supplicant_connection_timeout_cb * @@ -3222,8 +3318,19 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) static void ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP *ap) { - guint32 a_freqs[] = {5180, 5200, 5220, 5745, 5765, 5785, 5805, 0}; - guint32 bg_freqs[] = {2412, 2437, 2462, 2472, 0}; + guint32 freqs_a[] = {5180, /* only U-NII-1 channels: non-DFS and available everywhere */ + 5200, + 5220, + 5240, + 0}; + guint32 freqs_bg[] = {2412, 2437, 2462, 2472, 0}; + guint32 freqs_6ghz[] = {5975, /* only U-NII-5 PSC channels, for better compatibility */ + 6055, + 6135, + 6215, + 6295, + 6375, + 0}; guint32 *rnd_freqs; guint rnd_freqs_len; NMDevice *device = NM_DEVICE(self); @@ -3234,7 +3341,7 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP guint l; nm_assert(ap); - nm_assert(NM_IN_STRSET(band, NULL, "a", "bg")); + nm_assert(NM_IN_STRSET(band, NULL, "a", "bg", "6GHz")); if (nm_wifi_ap_get_freq(ap)) return; @@ -3268,11 +3375,14 @@ ensure_hotspot_frequency(NMDeviceWifi *self, NMSettingWireless *s_wifi, NMWifiAP } if (nm_streq0(band, "a")) { - rnd_freqs = a_freqs; - rnd_freqs_len = G_N_ELEMENTS(a_freqs) - 1; + rnd_freqs = freqs_a; + rnd_freqs_len = G_N_ELEMENTS(freqs_a) - 1; + } else if (nm_streq0(band, "6GHz")) { + rnd_freqs = freqs_6ghz; + rnd_freqs_len = G_N_ELEMENTS(freqs_6ghz) - 1; } else { - rnd_freqs = bg_freqs; - rnd_freqs_len = G_N_ELEMENTS(bg_freqs) - 1; + rnd_freqs = freqs_bg; + rnd_freqs_len = G_N_ELEMENTS(freqs_bg) - 1; } /* shuffle the frequencies (inplace). The idea is to choose diff --git a/src/core/devices/wifi/nm-wifi-ap.c b/src/core/devices/wifi/nm-wifi-ap.c index ceb954b7..3e758972 100644 --- a/src/core/devices/wifi/nm-wifi-ap.c +++ b/src/core/devices/wifi/nm-wifi-ap.c @@ -574,16 +574,6 @@ nm_wifi_ap_to_string(const NMWifiAP *self, char *str_buf, gulong buf_len, gint64 return str_buf; } -static guint -freq_to_band(guint32 freq) -{ - if (freq >= 4915 && freq <= 5825) - return 5; - else if (freq >= 2412 && freq <= 2484) - return 2; - return 0; -} - gboolean nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection) { @@ -631,12 +621,12 @@ nm_wifi_ap_check_compatible(NMWifiAP *self, NMConnection *connection) band = nm_setting_wireless_get_band(s_wireless); if (band) { - guint ap_band = freq_to_band(priv->freq); + const char *ap_band = nm_wifi_freq_to_band_prop(priv->freq); - if (!strcmp(band, "a") && ap_band != 5) - return FALSE; - else if (!strcmp(band, "bg") && ap_band != 2) + if (!nm_streq(band, ap_band)) return FALSE; + + return TRUE; } channel = nm_setting_wireless_get_channel(s_wireless); diff --git a/src/core/devices/wifi/nm-wifi-utils.c b/src/core/devices/wifi/nm-wifi-utils.c index 332352ab..2ee4ec2e 100644 --- a/src/core/devices/wifi/nm-wifi-utils.c +++ b/src/core/devices/wifi/nm-wifi-utils.c @@ -639,7 +639,7 @@ nm_wifi_utils_complete_connection(GBytes *ap_ssid, chan_valid = FALSE; } - band = nm_utils_wifi_freq_to_band(ap_freq); + band = nm_wifi_freq_to_band_prop(ap_freq); if (band) { g_object_set(s_wifi, NM_SETTING_WIRELESS_BAND, band, NULL); } else { @@ -890,6 +890,28 @@ nm_wifi_utils_is_manf_default_ssid(GBytes *ssid) return FALSE; } +/* Convert a WPS "Key" credential into a PSK string. The key is either an + * 8..63 character passphrase or a 64 character hexadecimal PSK. The actual + * WPA-PSK validity check is shared with nm_utils_wpa_psk_valid(). */ +gboolean +nm_wifi_utils_wps_key_to_psk(const guint8 *key, gsize key_len, char (*out_psk)[65]) +{ + if (key_len > 64) + return FALSE; + if (key_len < 64 && !g_utf8_validate((const char *) key, key_len, NULL)) + return FALSE; + + memcpy(*out_psk, key, key_len); + (*out_psk)[key_len] = '\0'; + + /* An embedded NUL would make nm_utils_wpa_psk_valid() see a truncated + * string, so reject it explicitly. */ + if (strlen(*out_psk) != key_len) + return FALSE; + + return nm_utils_wpa_psk_valid(*out_psk); +} + /* To be used for connections where the SSID has been validated before */ gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *connection, @@ -1929,3 +1951,19 @@ nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b) return a->source == b->source && a->sink == b->sink && a->port == b->port && a->has_audio == b->has_audio && a->has_uibc == b->has_uibc && a->has_cp == b->has_cp; } + +const char * +nm_wifi_freq_to_band_prop(guint32 freq) +{ + switch (nm_utils_wifi_freq_to_band(freq)) { + case NM_WIFI_BAND_2_4_GHZ: + return "bg"; + case NM_WIFI_BAND_5_GHZ: + return "a"; + case NM_WIFI_BAND_6_GHZ: + return "6GHz"; + default: + case NM_WIFI_BAND_UNKNOWN: + return NULL; + } +} diff --git a/src/core/devices/wifi/nm-wifi-utils.h b/src/core/devices/wifi/nm-wifi-utils.h index 1d46a900..30920396 100644 --- a/src/core/devices/wifi/nm-wifi-utils.h +++ b/src/core/devices/wifi/nm-wifi-utils.h @@ -42,6 +42,8 @@ gboolean nm_wifi_utils_complete_connection(GBytes *ssid, gboolean nm_wifi_utils_is_manf_default_ssid(GBytes *ssid); +gboolean nm_wifi_utils_wps_key_to_psk(const guint8 *key, gsize key_len, char (*out_psk)[65]); + gboolean nm_wifi_connection_get_iwd_ssid_and_security(NMConnection *connection, char **ssid, NMIwdNetworkSecurity *security); @@ -56,4 +58,6 @@ bool nm_wifi_utils_parse_wfd_ies(GBytes *ies, NMIwdWfdInfo *out_wfd); GBytes *nm_wifi_utils_build_wfd_ies(const NMIwdWfdInfo *wfd); bool nm_wifi_utils_wfd_info_eq(const NMIwdWfdInfo *a, const NMIwdWfdInfo *b); +const char *nm_wifi_freq_to_band_prop(guint32 freq); + #endif /* __NM_WIFI_UTILS_H__ */ diff --git a/src/core/devices/wifi/tests/test-devices-wifi.c b/src/core/devices/wifi/tests/test-devices-wifi.c index a52696ea..b50e2626 100644 --- a/src/core/devices/wifi/tests/test-devices-wifi.c +++ b/src/core/devices/wifi/tests/test-devices-wifi.c @@ -1459,6 +1459,50 @@ test_ssids_options_to_ptrarray(void) /*****************************************************************************/ +static void +test_wps_key_to_psk(void) +{ + static const struct { + const char *key; + gsize key_len; + gboolean valid; + } cases[] = { + {"12345678", 8, TRUE}, + {"supersecret123", 14, TRUE}, + {"caf\xc3\xa9_key", 9, TRUE}, + {"123456789012345678901234567890123456789012345678901234567890123", 63, TRUE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", 64, TRUE}, + {"0123456789ABCDEF0123456789abcdef0123456789abcdef0123456789abcdeF", 64, TRUE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdeg", 64, FALSE}, + {"", 0, FALSE}, + {"1234567", 7, FALSE}, + {"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0", 65, FALSE}, + {"abc\xff" + "def123", + 10, + FALSE}, + {"0123456789abcdef0123456789abcdef\0" + "23456789abcdef0123456789abcdef0", + 64, + FALSE}, + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS(cases); i++) { + char psk[65]; + gboolean ok; + + ok = nm_wifi_utils_wps_key_to_psk((const guint8 *) cases[i].key, cases[i].key_len, &psk); + g_assert_cmpint(ok, ==, cases[i].valid); + if (ok) { + g_assert_cmpmem(psk, cases[i].key_len, cases[i].key, cases[i].key_len); + g_assert_cmpint(psk[cases[i].key_len], ==, '\0'); + } + } +} + +/*****************************************************************************/ + NMTST_DEFINE(); int @@ -1605,5 +1649,7 @@ main(int argc, char **argv) g_test_add_func("/wifi/ssids_options_to_ptrarray", test_ssids_options_to_ptrarray); + g_test_add_func("/wifi/wps_key_to_psk", test_wps_key_to_psk); + return g_test_run(); } diff --git a/src/core/devices/wwan/nm-modem-manager.c b/src/core/devices/wwan/nm-modem-manager.c index 4a89f38e..b8438e3f 100644 --- a/src/core/devices/wwan/nm-modem-manager.c +++ b/src/core/devices/wwan/nm-modem-manager.c @@ -258,16 +258,7 @@ modm_handle_name_owner_changed(MMManager *modem_manager, GParamSpec *pspec, NMMo /* Available! */ g_free(name_owner); - /* Hack alert: GDBusObjectManagerClient won't signal neither 'object-added' - * nor 'object-removed' if it was created while there was no ModemManager in - * the bus. This hack avoids this issue until we get a GIO with the fix - * included... */ - modm_clear_manager(self); - modm_ensure_manager(self); - - /* Whenever GDBusObjectManagerClient is fixed, we can just do the following: - * modm_manager_available (self); - */ + modm_manager_available(self); } static void |