diff options
Diffstat (limited to 'src/core/devices')
36 files changed, 1307 insertions, 723 deletions
diff --git a/src/core/devices/adsl/nm-device-adsl.c b/src/core/devices/adsl/nm-device-adsl.c index fcd16b1c..89bc84d8 100644 --- a/src/core/devices/adsl/nm-device-adsl.c +++ b/src/core/devices/adsl/nm-device-adsl.c @@ -67,13 +67,16 @@ get_generic_capabilities(NMDevice *dev) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingAdsl *s_adsl; const char *protocol; if (!NM_DEVICE_CLASS(nm_device_adsl_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_adsl = nm_connection_get_setting_adsl(connection); diff --git a/src/core/devices/bluetooth/nm-device-bt.c b/src/core/devices/bluetooth/nm-device-bt.c index a8258123..8b13e97d 100644 --- a/src/core/devices/bluetooth/nm-device-bt.c +++ b/src/core/devices/bluetooth/nm-device-bt.c @@ -192,7 +192,10 @@ can_auto_connect(NMDevice *device, NMSettingsConnection *sett_conn, char **speci } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceBt *self = NM_DEVICE_BT(device); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE(self); @@ -200,7 +203,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * const char *bdaddr; if (!NM_DEVICE_CLASS(nm_device_bt_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; if (!get_connection_bt_type_check(self, connection, NULL, error)) diff --git a/src/core/devices/nm-device-6lowpan.c b/src/core/devices/nm-device-6lowpan.c index 870a1c14..f3386ddb 100644 --- a/src/core/devices/nm-device-6lowpan.c +++ b/src/core/devices/nm-device-6lowpan.c @@ -72,14 +72,22 @@ create_and_realize(NMDevice *device, s_6lowpan = NM_SETTING_6LOWPAN(nm_connection_get_setting(connection, NM_TYPE_SETTING_6LOWPAN)); g_return_val_if_fail(s_6lowpan, FALSE); - parent_ifindex = parent ? nm_device_get_ifindex(parent) : 0; + if (!parent) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "6LoWPAN device can not be created without a parent interface"); + return FALSE; + } + parent_ifindex = nm_device_get_ifindex(parent); if (parent_ifindex <= 0) { g_set_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, - "6LoWPAN devices can not be created without a parent interface"); - g_return_val_if_fail(!parent, FALSE); + "cannot retrieve ifindex of interface %s (%s)", + nm_device_get_iface(parent), + nm_device_get_type_desc(parent)); return FALSE; } diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 56c5ec50..10fe8092 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -39,7 +39,8 @@ NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT, \ NM_SETTING_BOND_OPTION_RESEND_IGMP, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB, \ 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_NUM_GRAT_ARP, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY, \ + NM_SETTING_BOND_OPTION_ARP_MISSED_MAX, NM_SETTING_BOND_OPTION_LACP_ACTIVE #define OPTIONS_REAPPLY_SUBSET \ NM_SETTING_BOND_OPTION_MIIMON, NM_SETTING_BOND_OPTION_UPDELAY, \ @@ -51,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_PEER_NOTIF_DELAY, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX, \ + NM_SETTING_BOND_OPTION_LACP_ACTIVE #define OPTIONS_REAPPLY_FULL \ OPTIONS_REAPPLY_SUBSET, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, \ - NM_SETTING_BOND_OPTION_ARP_IP_TARGET + NM_SETTING_BOND_OPTION_ARP_IP_TARGET, NM_SETTING_BOND_OPTION_NS_IP6_TARGET /*****************************************************************************/ @@ -267,7 +269,7 @@ set_arp_targets(NMDevice *device, const char *cur_arp_ip_target, const char *new cur_strv = nm_strsplit_set_full(cur_arp_ip_target, NM_ASCII_SPACES, NM_STRSPLIT_SET_FLAGS_STRSTRIP); - new_strv = nm_utils_bond_option_arp_ip_targets_split(new_arp_ip_target); + new_strv = nm_utils_bond_option_ip_split(new_arp_ip_target); cur_len = NM_PTRARRAY_LEN(cur_strv); new_len = NM_PTRARRAY_LEN(new_strv); @@ -364,7 +366,7 @@ _bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_ int i; int added = 0; - ip = nm_utils_bond_option_arp_ip_targets_split(value); + ip = nm_utils_bond_option_ip_split(value); if (!ip) return added; @@ -380,6 +382,31 @@ _bond_arp_ip_target_to_platform(const char *value, in_addr_t out[static NM_BOND_ return added; } +static guint8 +_bond_ns_ip6_target_to_platform(const char *value, + struct in6_addr out[static NM_BOND_MAX_ARP_TARGETS]) +{ + gs_free const char **ip = NULL; + struct in6_addr in6_a; + int i; + int added = 0; + + ip = nm_utils_bond_option_ip_split(value); + + if (!ip) + return added; + + for (i = 0; ip[i]; i++) { + if (added > NM_BOND_MAX_ARP_TARGETS - 1) + break; + if (!nm_inet_parse_bin(AF_INET6, ip[i], NULL, &in6_a)) + nm_assert_not_reached(); /* verify() already validated the IP addresses */ + + out[added++] = in6_a; + } + return added; +} + static int _setting_bond_primary_opt_as_ifindex(NMSettingBond *s_bond) { @@ -436,6 +463,10 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY), .num_grat_arp = _v_u8(s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP), .all_ports_active = _v_u8(s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE), + .arp_missed_max = _v_u8(s_bond, NM_SETTING_BOND_OPTION_ARP_MISSED_MAX), + .lacp_active = _v_fcn(_nm_setting_bond_lacp_active_from_string, + s_bond, + NM_SETTING_BOND_OPTION_LACP_ACTIVE), .lacp_rate = _v_fcn(_nm_setting_bond_lacp_rate_from_string, s_bond, NM_SETTING_BOND_OPTION_LACP_RATE), @@ -455,6 +486,11 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p props->arp_ip_targets_num = _bond_arp_ip_target_to_platform(opt_value, props->arp_ip_target); + opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_NS_IP6_TARGET); + if (opt_value != NULL) + props->ns_ip6_targets_num = + _bond_ns_ip6_target_to_platform(opt_value, props->ns_ip6_target); + props->miimon_has = !props->arp_interval && !props->arp_validate; props->updelay_has = props->miimon_has && props->miimon; props->downdelay_has = props->miimon_has && props->miimon; @@ -462,6 +498,7 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p props->resend_igmp_has = props->resend_igmp != 1; 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); } static void @@ -639,12 +676,14 @@ commit_port_options(NMDevice *bond_device, NMDevice *port, NMSettingBondPort *s_ nm_platform_link_change(nm_device_get_platform(port), nm_device_get_ifindex(port), + NULL, &((NMPlatformLinkBondPort){ .queue_id = s_port ? nm_setting_bond_port_get_queue_id(s_port) : NM_BOND_PORT_QUEUE_ID_DEF, .prio = prio_has ? prio : 0, .prio_has = prio_has, - })); + }), + 0); } static NMTernary @@ -686,8 +725,13 @@ attach_port(NMDevice *device, return TRUE; } -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceBond *self = NM_DEVICE_BOND(device); gboolean success; @@ -749,6 +793,8 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) _LOGI(LOGD_BOND, "bond port %s was detached", nm_device_get_ip_iface(port)); } } + + return TRUE; } static gboolean diff --git a/src/core/devices/nm-device-bridge.c b/src/core/devices/nm-device-bridge.c index c5ce34c2..9a45dbf3 100644 --- a/src/core/devices/nm-device-bridge.c +++ b/src/core/devices/nm-device-bridge.c @@ -101,13 +101,16 @@ check_connection_available(NMDevice *device, } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingBridge *s_bridge; const char *mac_address; if (!NM_DEVICE_CLASS(nm_device_bridge_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; if (nm_connection_is_type(connection, NM_SETTING_BLUETOOTH_SETTING_NAME) @@ -434,96 +437,6 @@ static const Option slave_options[] = { OPTION(NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "hairpin_mode", OPTION_TYPE_BOOL(FALSE), ), {0}}; -static void -commit_option(NMDevice *device, NMSetting *setting, const Option *option, gboolean slave) -{ - int ifindex = nm_device_get_ifindex(device); - nm_auto_unset_gvalue GValue val = G_VALUE_INIT; - GParamSpec *pspec; - const char *value; - char value_buf[100]; - - if (slave) - nm_assert(NM_IS_SETTING_BRIDGE_PORT(setting)); - else - nm_assert(NM_IS_SETTING_BRIDGE(setting)); - - pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(setting), option->name); - nm_assert(pspec); - - g_value_init(&val, G_PARAM_SPEC_VALUE_TYPE(pspec)); - g_object_get_property((GObject *) setting, option->name, &val); - - if (option->to_sysfs) { - value = option->to_sysfs(&val); - goto out; - } - - switch (pspec->value_type) { - case G_TYPE_BOOLEAN: - value = g_value_get_boolean(&val) ? "1" : "0"; - break; - case G_TYPE_UINT64: - case G_TYPE_UINT: - { - guint64 uval; - - if (pspec->value_type == G_TYPE_UINT64) - uval = g_value_get_uint64(&val); - else - uval = (guint) g_value_get_uint(&val); - - /* zero means "unspecified" for some NM properties but isn't in the - * allowed kernel range, so reset the property to the default value. - */ - if (option->default_if_zero && uval == 0) { - if (pspec->value_type == G_TYPE_UINT64) - uval = NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(pspec); - else - uval = NM_G_PARAM_SPEC_GET_DEFAULT_UINT(pspec); - } - - /* Linux kernel bridge interfaces use 'centiseconds' for time-based values. - * In reality it's not centiseconds, but depends on HZ and USER_HZ, which - * is almost always works out to be a multiplier of 100, so we can assume - * centiseconds. See clock_t_to_jiffies(). - */ - if (option->user_hz_compensate) - uval *= 100; - - if (pspec->value_type == G_TYPE_UINT64) - nm_sprintf_buf(value_buf, "%" G_GUINT64_FORMAT, uval); - else - nm_sprintf_buf(value_buf, "%u", (guint) uval); - - value = value_buf; - } break; - case G_TYPE_STRING: - value = g_value_get_string(&val); - break; - default: - nm_assert_not_reached(); - value = NULL; - break; - } - -out: - if (!value) - return; - - if (slave) { - nm_platform_sysctl_slave_set_option(nm_device_get_platform(device), - ifindex, - option->sysname, - value); - } else { - nm_platform_sysctl_master_set_option(nm_device_get_platform(device), - ifindex, - option->sysname, - value); - } -} - static const NMPlatformBridgeVlan ** setting_vlans_to_platform(GPtrArray *array) { @@ -558,19 +471,92 @@ setting_vlans_to_platform(GPtrArray *array) } static void -commit_slave_options(NMDevice *device, NMSettingBridgePort *setting) +commit_port_options(NMDevice *device, NMSettingBridgePort *setting) { const Option *option; NMSetting *s; gs_unref_object NMSetting *s_clear = NULL; + int ifindex = nm_device_get_ifindex(device); if (setting) s = NM_SETTING(setting); else s = s_clear = nm_setting_bridge_port_new(); - for (option = slave_options; option->name; option++) - commit_option(device, s, option, TRUE); + for (option = slave_options; option->name; option++) { + nm_auto_unset_gvalue GValue val = G_VALUE_INIT; + GParamSpec *pspec; + const char *value; + char value_buf[100]; + + pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(s), option->name); + nm_assert(pspec); + + g_value_init(&val, G_PARAM_SPEC_VALUE_TYPE(pspec)); + g_object_get_property((GObject *) s, option->name, &val); + + if (option->to_sysfs) { + value = option->to_sysfs(&val); + goto out; + } + + switch (pspec->value_type) { + case G_TYPE_BOOLEAN: + value = g_value_get_boolean(&val) ? "1" : "0"; + break; + case G_TYPE_UINT64: + case G_TYPE_UINT: + { + guint64 uval; + + if (pspec->value_type == G_TYPE_UINT64) + uval = g_value_get_uint64(&val); + else + uval = (guint) g_value_get_uint(&val); + + /* zero means "unspecified" for some NM properties but isn't in the + * allowed kernel range, so reset the property to the default value. + */ + if (option->default_if_zero && uval == 0) { + if (pspec->value_type == G_TYPE_UINT64) + uval = NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(pspec); + else + uval = NM_G_PARAM_SPEC_GET_DEFAULT_UINT(pspec); + } + + /* Linux kernel bridge interfaces use 'centiseconds' for time-based values. + * In reality it's not centiseconds, but depends on HZ and USER_HZ, which + * is almost always works out to be a multiplier of 100, so we can assume + * centiseconds. See clock_t_to_jiffies(). + */ + if (option->user_hz_compensate) + uval *= 100; + + if (pspec->value_type == G_TYPE_UINT64) + nm_sprintf_buf(value_buf, "%" G_GUINT64_FORMAT, uval); + else + nm_sprintf_buf(value_buf, "%u", (guint) uval); + + value = value_buf; + } break; + case G_TYPE_STRING: + value = g_value_get_string(&val); + break; + default: + nm_assert_not_reached(); + value = NULL; + break; + } + +out: + if (!value) + return; + + nm_platform_sysctl_slave_set_option(nm_device_get_platform(device), + ifindex, + option->sysname, + value); + } } static void @@ -746,8 +732,13 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge) enabled = nm_setting_bridge_get_vlan_filtering(s_bridge); if (!enabled) { - nm_platform_sysctl_master_set_option(plat, ifindex, "vlan_filtering", "0"); - nm_platform_sysctl_master_set_option(plat, ifindex, "default_pvid", "1"); + nm_platform_link_set_bridge_info( + plat, + ifindex, + &((NMPlatformLinkSetBridgeInfoData){.vlan_filtering_has = TRUE, + .vlan_filtering_val = FALSE, + .vlan_default_pvid_has = TRUE, + .vlan_default_pvid_val = 1})); nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL); return TRUE; } @@ -762,14 +753,17 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge) self->vlan_configured = TRUE; - /* Filtering must be disabled to change the default PVID */ - if (!nm_platform_sysctl_master_set_option(plat, ifindex, "vlan_filtering", "0")) - return FALSE; - - /* Clear the default PVID so that we later can force the re-creation of + /* Filtering must be disabled to change the default PVID. + * Clear the default PVID so that we later can force the re-creation of * default PVID VLANs by writing the option again. */ - if (!nm_platform_sysctl_master_set_option(plat, ifindex, "default_pvid", "0")) - return FALSE; + + nm_platform_link_set_bridge_info( + plat, + ifindex, + &((NMPlatformLinkSetBridgeInfoData){.vlan_filtering_has = TRUE, + .vlan_filtering_val = FALSE, + .vlan_default_pvid_has = TRUE, + .vlan_default_pvid_val = 0})); /* Clear all existing VLANs */ if (!nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, NULL)) @@ -779,11 +773,11 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge) * a PVID VLAN on each port, including the bridge itself. */ pvid = nm_setting_bridge_get_vlan_default_pvid(s_bridge); if (pvid) { - char value[32]; - - nm_sprintf_buf(value, "%u", pvid); - if (!nm_platform_sysctl_master_set_option(plat, ifindex, "default_pvid", value)) - return FALSE; + nm_platform_link_set_bridge_info( + plat, + ifindex, + &((NMPlatformLinkSetBridgeInfoData){.vlan_default_pvid_has = TRUE, + .vlan_default_pvid_val = pvid})); } /* Create VLANs only after setting the default PVID, so that @@ -793,8 +787,12 @@ bridge_set_vlan_options(NMDevice *device, NMSettingBridge *s_bridge) if (plat_vlans && !nm_platform_link_set_bridge_vlans(plat, ifindex, FALSE, plat_vlans)) return FALSE; - if (!nm_platform_sysctl_master_set_option(plat, ifindex, "vlan_filtering", "1")) - return FALSE; + nm_platform_link_set_bridge_info(plat, + ifindex, + &((NMPlatformLinkSetBridgeInfoData){ + .vlan_filtering_has = TRUE, + .vlan_filtering_val = TRUE, + })); return TRUE; } @@ -1027,7 +1025,7 @@ attach_port(NMDevice *device, return FALSE; } - commit_slave_options(port, s_port); + commit_port_options(port, s_port); _LOGI(LOGD_BRIDGE, "attached bridge port %s", nm_device_get_ip_iface(port)); } else { @@ -1037,8 +1035,13 @@ attach_port(NMDevice *device, return TRUE; } -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceBridge *self = NM_DEVICE_BRIDGE(device); gboolean success; @@ -1055,7 +1058,7 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) if (ifindex_slave <= 0) { _LOGD(LOGD_TEAM, "bridge port %s is already detached", nm_device_get_ip_iface(port)); - return; + return TRUE; } if (configure) { @@ -1071,6 +1074,8 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) } else { _LOGI(LOGD_BRIDGE, "bridge port %s was detached", nm_device_get_ip_iface(port)); } + + return TRUE; } static gboolean diff --git a/src/core/devices/nm-device-ethernet.c b/src/core/devices/nm-device-ethernet.c index 97cf84a1..aedacc24 100644 --- a/src/core/devices/nm-device-ethernet.c +++ b/src/core/devices/nm-device-ethernet.c @@ -344,13 +344,16 @@ match_subchans(NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_ma } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceEthernet *self = NM_DEVICE_ETHERNET(device); NMSettingWired *s_wired; if (!NM_DEVICE_CLASS(nm_device_ethernet_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; if (nm_connection_is_type(connection, NM_SETTING_PPPOE_SETTING_NAME) diff --git a/src/core/devices/nm-device-factory.h b/src/core/devices/nm-device-factory.h index ac5ae05f..fc3d9dd4 100644 --- a/src/core/devices/nm-device-factory.h +++ b/src/core/devices/nm-device-factory.h @@ -208,8 +208,7 @@ NMDevice *nm_device_factory_create_device(NMDeviceFactory *factory, \ NM_DEVICE_FACTORY_DECLARE_TYPES(st_code) \ \ - static void nm_##lower##_device_factory_init(NM##mixed##DeviceFactory *self) \ - {} \ + static void nm_##lower##_device_factory_init(NM##mixed##DeviceFactory *self) {} \ \ static void nm_##lower##_device_factory_class_init(NM##mixed##DeviceFactoryClass *klass) \ { \ diff --git a/src/core/devices/nm-device-generic.c b/src/core/devices/nm-device-generic.c index 9f85925b..c0dcf0de 100644 --- a/src/core/devices/nm-device-generic.c +++ b/src/core/devices/nm-device-generic.c @@ -16,7 +16,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_TYPE_DESCRIPTION, ); typedef struct { - char *type_description; + const char *type_description; } NMDeviceGenericPrivate; struct _NMDeviceGeneric { @@ -64,20 +64,23 @@ realize_start_notify(NMDevice *device, const NMPlatformLink *plink) NM_DEVICE_CLASS(nm_device_generic_parent_class)->realize_start_notify(device, plink); - nm_clear_g_free(&priv->type_description); ifindex = nm_device_get_ip_ifindex(NM_DEVICE(self)); - if (ifindex > 0) + if (ifindex > 0) { priv->type_description = - g_strdup(nm_platform_link_get_type_name(nm_device_get_platform(device), ifindex)); + nm_platform_link_get_type_name(nm_device_get_platform(device), ifindex); + } } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingConnection *s_con; if (!NM_DEVICE_CLASS(nm_device_generic_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_con = nm_connection_get_setting_connection(connection); @@ -125,22 +128,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) } } -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMDeviceGeneric *self = NM_DEVICE_GENERIC(object); - NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_TYPE_DESCRIPTION: - priv->type_description = g_value_dup_string(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - /*****************************************************************************/ static void @@ -177,17 +164,6 @@ nm_device_generic_new(const NMPlatformLink *plink, gboolean nm_plugin_missing) NULL); } -static void -dispose(GObject *object) -{ - NMDeviceGeneric *self = NM_DEVICE_GENERIC(object); - NMDeviceGenericPrivate *priv = NM_DEVICE_GENERIC_GET_PRIVATE(self); - - nm_clear_g_free(&priv->type_description); - - G_OBJECT_CLASS(nm_device_generic_parent_class)->dispose(object); -} - static const NMDBusInterfaceInfoExtended interface_info_device_generic = { .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( NM_DBUS_INTERFACE_DEVICE_GENERIC, @@ -207,9 +183,7 @@ nm_device_generic_class_init(NMDeviceGenericClass *klass) NMDeviceClass *device_class = NM_DEVICE_CLASS(klass); object_class->constructor = constructor; - object_class->dispose = dispose; object_class->get_property = get_property; - object_class->set_property = set_property; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS(&interface_info_device_generic); @@ -228,7 +202,7 @@ nm_device_generic_class_init(NMDeviceGenericClass *klass) "", "", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } diff --git a/src/core/devices/nm-device-infiniband.c b/src/core/devices/nm-device-infiniband.c index d025931e..6ce61d0b 100644 --- a/src/core/devices/nm-device-infiniband.c +++ b/src/core/devices/nm-device-infiniband.c @@ -87,7 +87,7 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) /* With some drivers the interface must be down to set transport mode */ nm_device_take_down(device, TRUE); ok = nm_platform_sysctl_set(nm_device_get_platform(device), - NMP_SYSCTL_PATHID_NETDIR(dirfd, ifname_verified, "mode"), + NMP_SYSCTL_PATHID_NETDIR_A(dirfd, ifname_verified, "mode"), transport_mode); nm_device_bring_up(device); @@ -108,15 +108,18 @@ get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *ou } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingInfiniband *s_infiniband; if (!NM_DEVICE_CLASS(nm_device_infiniband_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { const char *mac; const char *hw_addr; diff --git a/src/core/devices/nm-device-ip-tunnel.c b/src/core/devices/nm-device-ip-tunnel.c index a5760bf4..cc62180e 100644 --- a/src/core/devices/nm-device-ip-tunnel.c +++ b/src/core/devices/nm-device-ip-tunnel.c @@ -498,7 +498,10 @@ update_connection(NMDevice *device, NMConnection *connection) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL(device); NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE(self); @@ -507,7 +510,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * const char *parent; if (!NM_DEVICE_CLASS(nm_device_ip_tunnel_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_ip_tunnel = nm_connection_get_setting_ip_tunnel(connection); @@ -520,7 +523,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * return FALSE; } - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { /* Check parent interface; could be an interface name or a UUID */ parent = nm_setting_ip_tunnel_get_parent(s_ip_tunnel); if (parent && !nm_device_match_parent(device, parent)) { diff --git a/src/core/devices/nm-device-logging.h b/src/core/devices/nm-device-logging.h index ffc90c2d..53330b5e 100644 --- a/src/core/devices/nm-device-logging.h +++ b/src/core/devices/nm-device-logging.h @@ -11,37 +11,37 @@ #if !_NM_CC_SUPPORT_GENERIC #define _NM_DEVICE_CAST(self) ((NMDevice *) (self)) #elif !defined(_NMLOG_DEVICE_TYPE) -#define _NM_DEVICE_CAST(self) _Generic((self), NMDevice * : ((NMDevice *) (self))) +#define _NM_DEVICE_CAST(self) _Generic((self), NMDevice *: ((NMDevice *) (self))) #else -#define _NM_DEVICE_CAST(self) \ - _Generic((self), _NMLOG_DEVICE_TYPE * \ - : ((NMDevice *) (self)), NMDevice * \ - : ((NMDevice *) (self))) +#define _NM_DEVICE_CAST(self) \ + _Generic((self), _NMLOG_DEVICE_TYPE *: ((NMDevice *) (self)), NMDevice *: ((NMDevice *) (self))) #endif #undef _NMLOG_ENABLED #define _NMLOG_ENABLED(level, domain) (nm_logging_enabled((level), (domain))) -#define _NMLOG(level, domain, ...) \ - G_STMT_START \ - { \ - const NMLogLevel _level = (level); \ - const NMLogDomain _domain = (domain); \ - \ - if (nm_logging_enabled(_level, _domain)) { \ - typeof(*self) *const _self = (self); \ - const char *const _ifname = _nm_device_get_iface(_NM_DEVICE_CAST(_self)); \ - \ - nm_log_obj(_level, \ - _domain, \ - _ifname, \ - NULL, \ - _self, \ - "device", \ - "%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - NM_PRINT_FMT_QUOTED(_ifname, "(", _ifname, ")", "[null]") \ - _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ +#define _NMLOG(level, domain, ...) \ + G_STMT_START \ + { \ + const NMLogLevel _level = (level); \ + const NMLogDomain _domain = (domain); \ + \ + if (nm_logging_enabled(_level, _domain)) { \ + typeof(*self) *const _self = (self); \ + const char *const _ifname = _nm_device_get_iface(_NM_DEVICE_CAST(_self)); \ + const char *_type = nm_device_get_type_desc_for_log(_NM_DEVICE_CAST(_self)); \ + \ + nm_log_obj(_level, \ + _domain, \ + _ifname, \ + NULL, \ + _self, \ + "device", \ + "%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_PRINT_FMT_QUOTED(_ifname, "(", _ifname, ")", "[null]"), \ + NM_PRINT_FMT_QUOTED(_type, "[", _type, "]", "") \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ G_STMT_END #endif /* __NETWORKMANAGER_DEVICE_LOGGING_H__ */ diff --git a/src/core/devices/nm-device-macvlan.c b/src/core/devices/nm-device-macvlan.c index 3f57bfb1..8cdef0cf 100644 --- a/src/core/devices/nm-device-macvlan.c +++ b/src/core/devices/nm-device-macvlan.c @@ -208,14 +208,22 @@ create_and_realize(NMDevice *device, s_macvlan = nm_connection_get_setting_macvlan(connection); g_return_val_if_fail(s_macvlan, FALSE); - parent_ifindex = parent ? nm_device_get_ifindex(parent) : 0; + if (!parent) { + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_MISSING_DEPENDENCIES, + "MACVLAN device can not be created without a parent interface"); + return FALSE; + } + parent_ifindex = nm_device_get_ifindex(parent); if (parent_ifindex <= 0) { g_set_error(error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_MISSING_DEPENDENCIES, - "MACVLAN devices can not be created without a parent interface"); - g_return_val_if_fail(!parent, FALSE); + "cannot retrieve ifindex of interface %s (%s)", + nm_device_get_iface(parent), + nm_device_get_type_desc(parent)); return FALSE; } @@ -274,14 +282,17 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) /*****************************************************************************/ static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE(device); NMSettingMacvlan *s_macvlan; const char *parent = NULL; if (!NM_DEVICE_CLASS(nm_device_macvlan_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_macvlan = nm_connection_get_setting_macvlan(connection); @@ -300,7 +311,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * } /* Before the device is realized some properties will not be set */ - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { if (setting_mode_to_platform(nm_setting_macvlan_get_mode(s_macvlan)) != priv->props.mode) { nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, diff --git a/src/core/devices/nm-device-ppp.c b/src/core/devices/nm-device-ppp.c index 1860df3c..27566607 100644 --- a/src/core/devices/nm-device-ppp.c +++ b/src/core/devices/nm-device-ppp.c @@ -149,12 +149,15 @@ _ppp_mgr_callback(NMPppMgr *ppp_mgr, const NMPppMgrCallbackData *callback_data, /*****************************************************************************/ static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingPppoe *s_pppoe; if (!NM_DEVICE_CLASS(nm_device_ppp_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_pppoe = nm_connection_get_setting_pppoe(connection); diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h index c597e052..013bc7fd 100644 --- a/src/core/devices/nm-device-private.h +++ b/src/core/devices/nm-device-private.h @@ -29,10 +29,6 @@ enum NMActStageReturn { #define NM_DEVICE_CAP_INTERNAL_MASK 0xc0000000 -NMSettings *nm_device_get_settings(NMDevice *self); - -NMManager *nm_device_get_manager(NMDevice *self); - gboolean nm_device_set_ip_ifindex(NMDevice *self, int ifindex); gboolean nm_device_set_ip_iface(NMDevice *self, const char *iface); @@ -180,4 +176,6 @@ void nm_device_auth_request(NMDevice *self, NMManagerDeviceAuthRequestFunc callback, gpointer user_data); +void nm_device_link_properties_set(NMDevice *self, gboolean reapply); + #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/core/devices/nm-device-tun.c b/src/core/devices/nm-device-tun.c index cbea7d7d..430e62aa 100644 --- a/src/core/devices/nm-device-tun.c +++ b/src/core/devices/nm-device-tun.c @@ -284,7 +284,10 @@ _same_og(const char *str, gboolean og_valid, guint32 og_num) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceTun *self = NM_DEVICE_TUN(device); NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE(self); @@ -292,10 +295,10 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * NMSettingTun *s_tun; if (!NM_DEVICE_CLASS(nm_device_tun_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { switch (priv->props.type) { case IFF_TUN: mode = NM_SETTING_TUN_MODE_TUN; diff --git a/src/core/devices/nm-device-vlan.c b/src/core/devices/nm-device-vlan.c index feb011db..7849e724 100644 --- a/src/core/devices/nm-device-vlan.c +++ b/src/core/devices/nm-device-vlan.c @@ -303,17 +303,20 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) /*****************************************************************************/ static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE(device); NMSettingVlan *s_vlan; const char *parent; if (!NM_DEVICE_CLASS(nm_device_vlan_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { s_vlan = nm_connection_get_setting_vlan(connection); if (nm_setting_vlan_get_id(s_vlan) != priv->vlan_id) { diff --git a/src/core/devices/nm-device-vrf.c b/src/core/devices/nm-device-vrf.c index ad31f3a2..a13de1cb 100644 --- a/src/core/devices/nm-device-vrf.c +++ b/src/core/devices/nm-device-vrf.c @@ -142,16 +142,19 @@ create_and_realize(NMDevice *device, } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceVrfPrivate *priv = NM_DEVICE_VRF_GET_PRIVATE(device); NMSettingVrf *s_vrf; if (!NM_DEVICE_CLASS(nm_device_vrf_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { s_vrf = _nm_connection_get_setting(connection, NM_TYPE_SETTING_VRF); if (priv->props.table != nm_setting_vrf_get_table(s_vrf)) { @@ -238,8 +241,13 @@ attach_port(NMDevice *device, return TRUE; } -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceVrf *self = NM_DEVICE_VRF(device); gboolean success; @@ -274,6 +282,8 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) _LOGI(LOGD_DEVICE, "VRF port %s was detached", nm_device_get_ip_iface(port)); } } + + return TRUE; } /*****************************************************************************/ diff --git a/src/core/devices/nm-device-vxlan.c b/src/core/devices/nm-device-vxlan.c index 44a7be33..061ee3f2 100644 --- a/src/core/devices/nm-device-vxlan.c +++ b/src/core/devices/nm-device-vxlan.c @@ -238,17 +238,20 @@ address_matches(const char *candidate, in_addr_t addr4, struct in6_addr *addr6) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE(device); NMSettingVxlan *s_vxlan; const char *parent; if (!NM_DEVICE_CLASS(nm_device_vxlan_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; - if (nm_device_is_real(device)) { + if (check_properties && nm_device_is_real(device)) { s_vxlan = nm_connection_get_setting_vxlan(connection); parent = nm_setting_vxlan_get_parent(s_vxlan); diff --git a/src/core/devices/nm-device-wpan.c b/src/core/devices/nm-device-wpan.c index 98356ccf..282eea87 100644 --- a/src/core/devices/nm-device-wpan.c +++ b/src/core/devices/nm-device-wpan.c @@ -75,13 +75,16 @@ update_connection(NMDevice *device, NMConnection *connection) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingWpan *s_wpan; const char *mac, *hw_addr; if (!NM_DEVICE_CLASS(nm_device_wpan_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_wpan = NM_SETTING_WPAN(nm_connection_get_setting(connection, NM_TYPE_SETTING_WPAN)); diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 62a9ff1e..2038e2f2 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -91,8 +91,8 @@ #define GRACE_PERIOD_MULTIPLIER 2U -#define CARRIER_WAIT_TIME_MS 6000 -#define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000 +#define CARRIER_WAIT_TIME_MS 6000 +#define CARRIER_WAIT_TIME_AFTER_MTU_MSEC 10000 #define NM_DEVICE_AUTH_RETRIES_UNSET -1 #define NM_DEVICE_AUTH_RETRIES_INFINITY -2 @@ -132,11 +132,6 @@ typedef struct { } SlaveInfo; typedef struct { - NMDevice *device; - guint idle_add_id; -} DeleteOnDeactivateData; - -typedef struct { NMDevice *device; GCancellable *cancellable; NMPlatformAsyncCallback callback; @@ -335,7 +330,6 @@ enum { IP6_PREFIX_DELEGATED, IP6_SUBNET_NEEDED, REMOVED, - RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, DNS_LOOKUP_DONE, PLATFORM_ADDRESS_CHANGED, @@ -513,8 +507,8 @@ typedef struct _NMDevicePrivate { NMUnmanagedFlags unmanaged_mask; NMUnmanagedFlags unmanaged_flags; - DeleteOnDeactivateData - *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */ + + GSource *delete_on_deactivate_idle_source; GCancellable *deactivating_cancellable; @@ -542,10 +536,10 @@ typedef struct _NMDevicePrivate { /* Link stuff */ guint link_connected_id; guint link_disconnected_id; - guint carrier_defer_id; - guint carrier_wait_id; gulong config_changed_id; gulong ifindex_changed_id; + GSource *carrier_wait_source; + GSource *carrier_defer_source; guint32 mtu; guint32 ip6_mtu; /* FIXME(l3cfg) */ guint32 mtu_initial; @@ -559,9 +553,9 @@ typedef struct _NMDevicePrivate { * until taking action. * * When changing MTU, the device might take longer then that. So, whenever - * NM changes the MTU it sets @carrier_wait_until_ms to CARRIER_WAIT_TIME_AFTER_MTU_MS + * NM changes the MTU it sets @carrier_wait_until_msec to CARRIER_WAIT_TIME_AFTER_MTU_MSEC * in the future. This is used to extend the grace period in this particular case. */ - gint64 carrier_wait_until_ms; + gint64 carrier_wait_until_msec; union { struct { @@ -576,6 +570,8 @@ typedef struct _NMDevicePrivate { NMDeviceSysIfaceState sys_iface_state_; }; + NMDeviceSysIfaceState sys_iface_state_before_sleep; + bool carrier : 1; bool ignore_carrier : 1; @@ -599,6 +595,8 @@ typedef struct _NMDevicePrivate { bool tc_committed : 1; + bool link_props_set : 1; + NMDeviceStageState stage1_sriov_state : 3; char *current_stable_id; @@ -702,6 +700,10 @@ typedef struct _NMDevicePrivate { GHashTable *ip6_saved_properties; EthtoolState *ethtool_state; + struct { + NMPlatformLinkProps props; + NMPlatformLinkChangeFlags flags; + } link_props_state; /* master interface for bridge/bond/team slave */ NMDevice *master; @@ -743,6 +745,9 @@ typedef struct _NMDevicePrivate { guint check_delete_unrealized_id; guint32 interface_flags; + guint32 port_detach_count; + NMDeviceStateReason port_detach_reason; + struct { SriovOp *pending; /* SR-IOV operation currently running */ SriovOp *next; /* next SR-IOV operation scheduled */ @@ -863,6 +868,7 @@ 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); /*****************************************************************************/ @@ -2750,6 +2756,152 @@ _ethtool_state_set(NMDevice *self) priv->ethtool_state = g_steal_pointer(ðtool_state); } +static NMPlatformLinkChangeFlags +link_properties_fill_from_setting(NMDevice *self, NMPlatformLinkProps *props) +{ + NMPlatformLinkChangeFlags flags = NM_PLATFORM_LINK_CHANGE_NONE; + NMSettingLink *s_link; + gint64 v; + + *props = (NMPlatformLinkProps){}; + + s_link = nm_device_get_applied_setting(self, NM_TYPE_SETTING_LINK); + if (!s_link) + return 0; + + v = nm_setting_link_get_tx_queue_length(s_link); + if (v != -1) { + props->tx_queue_length = (guint32) v; + flags |= NM_PLATFORM_LINK_CHANGE_TX_QUEUE_LENGTH; + } + + v = nm_setting_link_get_gso_max_size(s_link); + if (v != -1) { + props->gso_max_size = (guint32) v; + flags |= NM_PLATFORM_LINK_CHANGE_GSO_MAX_SIZE; + } + + v = nm_setting_link_get_gso_max_segments(s_link); + if (v != -1) { + props->gso_max_segments = (guint32) v; + flags |= NM_PLATFORM_LINK_CHANGE_GSO_MAX_SEGMENTS; + } + + v = nm_setting_link_get_gro_max_size(s_link); + if (v != -1) { + props->gro_max_size = (guint32) v; + flags |= NM_PLATFORM_LINK_CHANGE_GRO_MAX_SIZE; + } + + return flags; +} + +void +nm_device_link_properties_set(NMDevice *self, gboolean reapply) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMPlatformLinkProps props; + NMPlatformLinkChangeFlags flags; + NMPlatform *platform; + const NMPlatformLink *plink; + int ifindex; + + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) + return; + + if (priv->link_props_set && !reapply) + return; + + priv->link_props_set = TRUE; + + flags = link_properties_fill_from_setting(self, &props); + + if (flags == NM_PLATFORM_LINK_CHANGE_NONE + && priv->link_props_state.flags == NM_PLATFORM_LINK_CHANGE_NONE) { + /* Nothing to set now, and nothing was set previously. */ + return; + } + + platform = nm_device_get_platform(self); + + if (priv->link_props_state.flags == NM_PLATFORM_LINK_CHANGE_NONE) { + /* It's the first time we reach here. Try to fetch the current + * link settings (reset them later). */ + plink = nm_platform_link_get(platform, ifindex); + if (plink) { + priv->link_props_state.props = plink->link_props; + priv->link_props_state.flags = flags; + } else { + /* Unknown properties. The "priv->link_props_state.flags" stays unset. + * It indicates that "priv->link_props_state.props" is unknown. */ + } + + } else { + /* From a previous call we have some "priv->link_props_state.flags" + * flags, which indicates that all link props are cached. Also add + * "flags" which are are going to set, to indicate that those flags + * will need to be reset later. */ + priv->link_props_state.flags |= flags; + } + +#define _RESET(_f, _field) \ + if (!NM_FLAGS_HAS(flags, (_f)) && NM_FLAGS_HAS(priv->link_props_state.flags, (_f))) { \ + props._field = priv->link_props_state.props._field; \ + priv->link_props_state.flags &= ~(_f); \ + flags |= (_f); \ + } + + /* During reapply, if we previously set some "priv->link_props_state.flags" + * but now not anymore (according to "flags"), then we reset the value now. + * + * We do this by copying the props field from "priv->link_props_state" to + * "props", reset the flag in "priv->link_props_state.flags" and set the + * flag in "flags" (for changing it). */ + _RESET(NM_PLATFORM_LINK_CHANGE_TX_QUEUE_LENGTH, tx_queue_length); + _RESET(NM_PLATFORM_LINK_CHANGE_GSO_MAX_SIZE, gso_max_size); + _RESET(NM_PLATFORM_LINK_CHANGE_GSO_MAX_SEGMENTS, gso_max_segments); + _RESET(NM_PLATFORM_LINK_CHANGE_GRO_MAX_SIZE, gro_max_size); + + if (nm_platform_link_change(platform, ifindex, &props, NULL, flags)) { + _LOGD(LOGD_DEVICE, "link properties successfully set"); + } else { + _LOGW(LOGD_DEVICE, "failure setting link properties"); + } +} + +static void +link_properties_reset(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + NMPlatform *platform; + int ifindex; + + if (priv->link_props_state.flags == 0) + goto out; + + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) + goto out; + + platform = nm_device_get_platform(self); + nm_assert(platform); + + if (nm_platform_link_change(platform, + ifindex, + &priv->link_props_state.props, + NULL, + priv->link_props_state.flags)) { + _LOGD(LOGD_DEVICE, "link properties successfully reset"); + } else { + _LOGW(LOGD_DEVICE, "failure resetting link properties"); + } + +out: + priv->link_props_set = FALSE; + priv->link_props_state.flags = 0; +} + /*****************************************************************************/ gboolean @@ -2892,6 +3044,7 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st nm_device_sys_iface_state_to_string(sys_iface_state)); priv->sys_iface_state_ = sys_iface_state; _dev_l3_cfg_commit_type_reset(self); + nm_device_l3cfg_commit(self, NM_L3_CFG_COMMIT_TYPE_AUTO, FALSE); } /* this function only sets a flag, no immediate actions are initiated. @@ -2901,6 +3054,22 @@ nm_device_sys_iface_state_set(NMDevice *self, NMDeviceSysIfaceState sys_iface_st nm_assert(priv->sys_iface_state == sys_iface_state); } +void +nm_device_notify_sleeping(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + priv->sys_iface_state_before_sleep = priv->sys_iface_state; +} + +NMDeviceSysIfaceState +nm_device_get_sys_iface_state_before_sleep(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + return priv->sys_iface_state_before_sleep; +} + static void _active_connection_set_state_flags_full(NMDevice *self, NMActivationStateFlags flags, @@ -3371,7 +3540,7 @@ _dev_ip_state_check(NMDevice *self, int addr_family) &s_is_pending, &s_is_failed); - has_tna = priv->l3cfg && nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, addr_family); + has_tna = priv->l3cfg && nm_l3cfg_has_failedobj_pending(priv->l3cfg, addr_family); if (has_tna) s_is_pending = TRUE; @@ -3816,9 +3985,7 @@ after_merge_flags: } static gboolean -_dev_l3_register_l3cds_add_config(NMDevice *self, - L3ConfigDataType l3cd_type, - NML3CfgConfigFlags flags) +_dev_l3_register_l3cds_add_config(NMDevice *self, L3ConfigDataType l3cd_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NML3ConfigMergeFlags merge_flags; @@ -3841,7 +4008,7 @@ _dev_l3_register_l3cds_add_config(NMDevice *self, _prop_get_ipvx_dns_priority(self, AF_INET6), acd_defend_type, acd_timeout_msec, - flags, + NM_L3CFG_CONFIG_FLAGS_NONE, merge_flags); } @@ -3849,7 +4016,6 @@ static gboolean _dev_l3_register_l3cds_set_one_full(NMDevice *self, L3ConfigDataType l3cd_type, const NML3ConfigData *l3cd, - NML3CfgConfigFlags flags, NMTernary commit_sync) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -3873,7 +4039,7 @@ _dev_l3_register_l3cds_set_one_full(NMDevice *self, if (priv->l3cfg) { if (priv->l3cds[l3cd_type].d) { - if (_dev_l3_register_l3cds_add_config(self, l3cd_type, flags)) + if (_dev_l3_register_l3cds_add_config(self, l3cd_type)) changed = TRUE; } @@ -3897,11 +4063,7 @@ _dev_l3_register_l3cds_set_one(NMDevice *self, const NML3ConfigData *l3cd, NMTernary commit_sync) { - return _dev_l3_register_l3cds_set_one_full(self, - l3cd_type, - l3cd, - NM_L3CFG_CONFIG_FLAGS_NONE, - commit_sync); + return _dev_l3_register_l3cds_set_one_full(self, l3cd_type, l3cd, commit_sync); } static void @@ -3956,7 +4118,7 @@ _dev_l3_register_l3cds(NMDevice *self, } if (is_external) continue; - if (_dev_l3_register_l3cds_add_config(self, i, NM_L3CFG_CONFIG_FLAGS_NONE)) + if (_dev_l3_register_l3cds_add_config(self, i)) changed = TRUE; } @@ -4110,6 +4272,7 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N _dev_ipshared4_spawn_dnsmasq(self); nm_clear_l3cd(&priv->ipshared_data_4.v4.l3cd); } + _dev_ip_state_check_async(self, AF_UNSPEC); _dev_ipmanual_check_ready(self); return; case NM_L3_CONFIG_NOTIFY_TYPE_IPV4LL_EVENT: @@ -4119,10 +4282,6 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N return; case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE: return; - case NM_L3_CONFIG_NOTIFY_TYPE_ROUTES_TEMPORARY_NOT_AVAILABLE_EXPIRED: - /* we commit again. This way we try to configure the routes.*/ - _dev_l3_cfg_commit(self, FALSE); - return; case NM_L3_CONFIG_NOTIFY_TYPE_PLATFORM_CHANGE_ON_IDLE: if (NM_FLAGS_ANY(notify_data->platform_change_on_idle.obj_type_flags, nmp_object_type_to_flags(NMP_OBJECT_TYPE_LINK) @@ -4154,9 +4313,6 @@ _dev_l3_cfg_notify_cb(NML3Cfg *l3cfg, const NML3ConfigNotifyData *notify_data, N * synchronously to update the current state and schedule a commit. */ nm_ndisc_dad_failed(priv->ipac6_data.ndisc, conflicts, TRUE); } else if (ready) { - if (nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, AF_INET6)) - _dev_l3_cfg_commit(self, FALSE); - nm_clear_l3cd(&priv->ipac6_data.l3cd); _dev_ipac6_set_state(self, NM_DEVICE_IP_STATE_READY); _dev_ip_state_check_async(self, AF_INET6); @@ -4736,6 +4892,7 @@ nm_device_parent_find_for_connection(NMDevice *self, const char *current_setting && nm_device_check_connection_compatible( parent_device, nm_settings_connection_get_connection(parent_connection), + TRUE, NULL)) return current_setting_parent; } @@ -4959,6 +5116,18 @@ nm_device_get_ip_iface_identifier(NMDevice *self, } const char * +nm_device_get_s390_subchannels(NMDevice *self) +{ + NMDeviceClass *klass; + + g_return_val_if_fail(NM_IS_DEVICE(self), NULL); + + klass = NM_DEVICE_GET_CLASS(self); + + return klass->get_s390_subchannels ? klass->get_s390_subchannels(self) : NULL; +} + +const char * nm_device_get_driver(NMDevice *self) { g_return_val_if_fail(self != NULL, NULL); @@ -5243,13 +5412,31 @@ nm_device_get_type_desc(NMDevice *self) } const char * +nm_device_get_type_desc_for_log(NMDevice *self) +{ + const char *type; + + type = nm_device_get_type_desc(self); + + /* Some OVS device types (ports and bridges) are not backed by a kernel link, and + * they can have the same name of another device of a different type. In fact, it's + * quite common to assign the same name to the OVS bridge, the OVS port and the OVS + * interface. For this reason, also log the type in case of OVS devices to make the + * log message unambiguous. */ + if (NM_STR_HAS_PREFIX(type, "Open vSwitch")) + return type; + + return NULL; +} + +const char * nm_device_get_type_description(NMDevice *self) { g_return_val_if_fail(self != NULL, NULL); /* Beware: this function should return the same - * value as nm_device_get_type_description() in libnm. */ - + * value as nm_device_get_type_description() in libnm. + * The returned string is static or interned */ return NM_DEVICE_GET_CLASS(self)->get_type_description(self); } @@ -6162,7 +6349,7 @@ attach_port_cb(NMDevice *self, GError *error, gpointer user_data) * nm_device_master_enslave_slave: * @self: the master device * @slave: the slave device to enslave - * @connection: (allow-none): the slave device's connection + * @connection: (nullable): the slave device's connection * * If @self is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function enslaves @slave. @@ -6206,6 +6393,21 @@ nm_device_master_enslave_slave(NMDevice *self, NMDevice *slave, NMConnection *co attach_port_done(self, slave, success); } +static void +detach_port_cb(NMDevice *self, GError *error, gpointer user_data) +{ + nm_auto_unref_object NMDevice *slave = user_data; + NMDevicePrivate *slave_priv = NM_DEVICE_GET_PRIVATE(slave); + + nm_assert(slave_priv->port_detach_count > 0); + + if (--slave_priv->port_detach_count == 0) { + if (slave_priv->state == NM_DEVICE_STATE_DEACTIVATING) { + deactivate_ready(slave, slave_priv->port_detach_reason); + } + } +} + /** * nm_device_master_release_slave: * @self: the master device @@ -6262,10 +6464,20 @@ nm_device_master_release_slave(NMDevice *self, /* first, let subclasses handle the release ... */ if (info->slave_is_enslaved || nm_device_sys_iface_state_is_external(slave) - || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE) - NM_DEVICE_GET_CLASS(self)->detach_port(self, - slave, - release_type >= RELEASE_SLAVE_TYPE_CONFIG); + || release_type >= RELEASE_SLAVE_TYPE_CONFIG_FORCE) { + NMTernary ret; + + ret = NM_DEVICE_GET_CLASS(self)->detach_port(self, + slave, + release_type >= RELEASE_SLAVE_TYPE_CONFIG, + NULL, + detach_port_cb, + g_object_ref(slave)); + if (ret == NM_TERNARY_DEFAULT) { + slave_priv->port_detach_count++; + slave_priv->port_detach_reason = reason; + } + } /* raise notifications about the release, including clearing is_enslaved. */ nm_device_slave_notify_release(slave, reason, release_type); @@ -6344,13 +6556,6 @@ _dev_unmanaged_check_external_down(NMDevice *self, gboolean only_if_unmanaged, g } ext_flags = _dev_unmanaged_is_external_down(self, FALSE); - if (ext_flags != NM_UNMAN_FLAG_OP_SET_UNMANAGED) { - /* Ensure the assume check is queued before any queued state changes - * from the transition to UNAVAILABLE. - */ - nm_device_queue_recheck_assume(self); - } - if (now) { nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_EXTERNAL_DOWN, @@ -6434,6 +6639,8 @@ carrier_changed(NMDevice *self, gboolean carrier) } if (carrier) { + gboolean recheck_auto_activate = FALSE; + if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { nm_device_queue_state(self, NM_DEVICE_STATE_DISCONNECTED, @@ -6444,8 +6651,18 @@ carrier_changed(NMDevice *self, gboolean carrier) * when the carrier appears, auto connections are rechecked for * the device. */ - nm_device_emit_recheck_auto_activate(self); + recheck_auto_activate = TRUE; } + if (nm_manager_devcon_autoconnect_blocked_reason_set( + nm_device_get_manager(self), + self, + NULL, + NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED, + FALSE)) + recheck_auto_activate = TRUE; + + if (recheck_auto_activate) + nm_device_recheck_auto_activate_schedule(self); } else { if (priv->state == NM_DEVICE_STATE_UNAVAILABLE) { if (priv->queued_state.id && priv->queued_state.state >= NM_DEVICE_STATE_DISCONNECTED) @@ -6464,24 +6681,20 @@ carrier_disconnected_action_cb(gpointer user_data) NMDevice *self = NM_DEVICE(user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - _LOGD(LOGD_DEVICE, - "carrier: link disconnected (calling deferred action) (id=%u)", - priv->carrier_defer_id); + _LOGD(LOGD_DEVICE, "carrier: link disconnected (calling deferred action)"); - priv->carrier_defer_id = 0; + nm_clear_g_source_inst(&priv->carrier_defer_source); carrier_changed(self, FALSE); - return FALSE; + return G_SOURCE_CONTINUE; } static void carrier_disconnected_action_cancel(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - guint id = priv->carrier_defer_id; - if (nm_clear_g_source(&priv->carrier_defer_id)) { - _LOGD(LOGD_DEVICE, "carrier: link disconnected (canceling deferred action) (id=%u)", id); - } + if (nm_clear_g_source_inst(&priv->carrier_defer_source)) + _LOGD(LOGD_DEVICE, "carrier: link disconnected (canceling deferred action)"); } void @@ -6509,28 +6722,29 @@ nm_device_set_carrier(NMDevice *self, gboolean carrier) NM_DEVICE_GET_CLASS(self)->carrier_changed_notify(self, carrier); carrier_changed(self, TRUE); - if (priv->carrier_wait_id) { + if (priv->carrier_wait_source) { nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); _carrier_wait_check_queued_act_request(self); } } else { - if (priv->carrier_wait_id) + if (priv->carrier_wait_source) nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); NM_DEVICE_GET_CLASS(self)->carrier_changed_notify(self, carrier); if (state <= NM_DEVICE_STATE_DISCONNECTED && !priv->queued_act_request) { _LOGD(LOGD_DEVICE, "carrier: link disconnected"); + carrier_disconnected_action_cancel(self); carrier_changed(self, FALSE); - } else { - gint64 now_ms, until_ms; + } else if (!priv->carrier_defer_source) { + gint64 until_ms; + gint64 now_ms; now_ms = nm_utils_get_monotonic_timestamp_msec(); - until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms); - priv->carrier_defer_id = - g_timeout_add(until_ms - now_ms, carrier_disconnected_action_cb, self); + until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_msec); + priv->carrier_defer_source = + nm_g_timeout_add_source(until_ms - now_ms, carrier_disconnected_action_cb, self); _LOGD(LOGD_DEVICE, - "carrier: link disconnected (deferring action for %ld milliseconds) (id=%u)", - (long) (until_ms - now_ms), - priv->carrier_defer_id); + "carrier: link disconnected (deferring action for %ld milliseconds)", + (long) (until_ms - now_ms)); } } } @@ -6682,6 +6896,37 @@ device_update_interface_flags(NMDevice *self, const NMPlatformLink *plink) TRUE); } +/* + * Returns the reason for managing a device. The suffix "external" indicates + * that the reason mainly depends on whether we want to make the device + * sys-iface-state=external or not. + */ +NMDeviceStateReason +nm_device_get_manage_reason_external(NMDevice *self) +{ + NMDeviceStateReason reason; + + /* By default we return reason NOW_MANAGED, which makes the device fully + * managed by NM (sys-iface-state=managed). */ + reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; + + /* If the device is an external-down candidate but no longer has the flag + * set, then the device is an externally created interface that previously + * had no addresses or no controller and now has. + * We need to set CONNECTION_ASSUMED as the reason, so that the device + * is managed but is not touched by NM (sys-iface-state=external). */ + if (nm_device_get_unmanaged_mask(self, NM_UNMANAGED_EXTERNAL_DOWN) + && !nm_device_get_unmanaged_flags(self, NM_UNMANAGED_EXTERNAL_DOWN)) { + /* user-udev overwrites external-down, so we only assume the device + * when it is a external-down candidate which is not managed via udev. */ + if (!nm_device_get_unmanaged_mask(self, NM_UNMANAGED_USER_UDEV)) { + reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED; + } + } + + return reason; +} + static gboolean device_link_changed(gpointer user_data) { @@ -6768,7 +7013,7 @@ device_link_changed(gpointer user_data) /* Let any connections that use the new interface name have a chance * to auto-activate on the device. */ - nm_device_emit_recheck_auto_activate(self); + nm_device_recheck_auto_activate_schedule(self); } if (priv->ipac6_data.ndisc && pllink->inet6_token.id) { @@ -6793,35 +7038,13 @@ device_link_changed(gpointer user_data) priv->up = NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP); if (pllink->initialized && nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) { - NMDeviceStateReason reason; - nm_device_set_unmanaged_by_user_udev(self); nm_device_set_unmanaged_by_user_conf(self); - reason = NM_DEVICE_STATE_REASON_NOW_MANAGED; - - /* If the device is a external-down candidated but no longer has external - * down set, we must clear the platform-unmanaged flag with reason - * "assumed". */ - if (nm_device_get_unmanaged_mask(self, NM_UNMANAGED_EXTERNAL_DOWN) - && !nm_device_get_unmanaged_flags(self, NM_UNMANAGED_EXTERNAL_DOWN)) { - /* actually, user-udev overwrites external-down. So we only assume the device, - * when it is a external-down candidate, which is not managed via udev. */ - if (!nm_device_get_unmanaged_mask(self, NM_UNMANAGED_USER_UDEV)) { - /* Ensure the assume check is queued before any queued state changes - * from the transition to UNAVAILABLE. - */ - reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED; - } - } - - /* The assume check should happen before the device transitions to - * UNAVAILABLE, because in UNAVAILABLE we already clean up the IP - * configuration. Therefore, this function should never trigger a - * sync state transition. - */ - nm_device_queue_recheck_assume(self); - nm_device_set_unmanaged_by_flags_queue(self, NM_UNMANAGED_PLATFORM_INIT, FALSE, reason); + nm_device_set_unmanaged_by_flags_queue(self, + NM_UNMANAGED_PLATFORM_INIT, + NM_UNMAN_FLAG_OP_SET_MANAGED, + nm_device_get_manage_reason_external(self)); } _dev_unmanaged_check_external_down(self, FALSE, FALSE); @@ -7309,14 +7532,15 @@ device_init_static_sriov_num_vfs(NMDevice *self) if (priv->ifindex > 0 && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) { int num_vfs; - num_vfs = nm_config_data_get_device_config_int64(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, - self, - 10, - 0, - G_MAXINT32, - -1, - -1); + num_vfs = nm_config_data_get_device_config_int64_by_device( + NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS, + self, + 10, + 0, + G_MAXINT32, + -1, + -1); if (num_vfs >= 0) sriov_op_queue(self, num_vfs, NM_OPTION_BOOL_DEFAULT, NULL, NULL); } @@ -7332,7 +7556,7 @@ config_changed(NMConfig *config, NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); if (priv->state <= NM_DEVICE_STATE_DISCONNECTED || priv->state >= NM_DEVICE_STATE_ACTIVATED) { - priv->ignore_carrier = nm_config_data_get_ignore_carrier(config_data, self); + priv->ignore_carrier = nm_config_data_get_ignore_carrier_by_device(config_data, self); if (NM_FLAGS_HAS(changes, NM_CONFIG_CHANGE_VALUES) && !nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV)) device_init_static_sriov_num_vfs(self); @@ -7471,8 +7695,9 @@ realize_start_setup(NMDevice *self, nm_device_update_permanent_hw_address(self, FALSE); /* Note: initial hardware address must be read before calling get_ignore_carrier() */ - config = nm_config_get(); - priv->ignore_carrier = nm_config_data_get_ignore_carrier(nm_config_get_data(config), self); + 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, @@ -7701,6 +7926,10 @@ nm_device_unrealize(NMDevice *self, gboolean remove_resources, GError **error) /* Garbage-collect unneeded unrealized devices. */ nm_device_recheck_available_connections(self); + /* In case the unrealized device is not going away, it may need to + * autoactivate. Schedule also a check for that. */ + nm_device_recheck_auto_activate_schedule(self); + return TRUE; } @@ -7721,7 +7950,7 @@ nm_device_notify_availability_maybe_changed(NMDevice *self) * available. */ nm_device_recheck_available_connections(self); if (g_hash_table_size(priv->available_connections) > 0) - nm_device_emit_recheck_auto_activate(self); + nm_device_recheck_auto_activate_schedule(self); } /** @@ -7880,7 +8109,7 @@ nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure) g_warn_if_fail(!NM_FLAGS_HAS(slave_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE)); nm_device_set_unmanaged_by_flags(slave, NM_UNMANAGED_IS_SLAVE, - FALSE, + NM_UNMAN_FLAG_OP_SET_MANAGED, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); changed = TRUE; } else @@ -8331,7 +8560,7 @@ nm_device_autoconnect_allowed(NMDevice *self) return FALSE; } - if (priv->delete_on_deactivate_data) + if (priv->delete_on_deactivate_idle_source) return FALSE; /* The 'autoconnect-allowed' signal is emitted on a device to allow @@ -8450,7 +8679,7 @@ device_has_config(NMDevice *self) * @self: the master #NMDevice * @slave: the slave #NMDevice * @connection: the #NMConnection to update with the slave settings - * @GError: (out): error description + * @error: error description * * Reads the slave configuration for @slave and updates @connection with those * properties. This invokes a virtual function on the master device @self. @@ -8719,7 +8948,7 @@ nm_device_complete_connection(NMDevice *self, if (!nm_connection_normalize(connection, NULL, NULL, error)) return FALSE; - return nm_device_check_connection_compatible(self, connection, error); + return nm_device_check_connection_compatible(self, connection, TRUE, error); } gboolean @@ -8779,7 +9008,10 @@ nm_device_match_parent_hwaddr(NMDevice *device, } static gboolean -check_connection_compatible(NMDevice *self, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *self, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); const char *device_iface = nm_device_get_iface(self); @@ -8906,12 +9138,18 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e * @self. */ gboolean -nm_device_check_connection_compatible(NMDevice *self, NMConnection *connection, GError **error) +nm_device_check_connection_compatible(NMDevice *self, + NMConnection *connection, + gboolean check_properties, + GError **error) { g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); g_return_val_if_fail(NM_IS_CONNECTION(connection), FALSE); - return NM_DEVICE_GET_CLASS(self)->check_connection_compatible(self, connection, error); + return NM_DEVICE_GET_CLASS(self)->check_connection_compatible(self, + connection, + check_properties, + error); } gboolean @@ -9077,9 +9315,9 @@ nm_device_queue_recheck_available(NMDevice *self, } void -nm_device_emit_recheck_auto_activate(NMDevice *self) +nm_device_recheck_auto_activate_schedule(NMDevice *self) { - g_signal_emit(self, signals[RECHECK_AUTO_ACTIVATE], 0); + nm_manager_device_recheck_auto_activate_schedule(nm_device_get_manager(self), self); } void @@ -9352,11 +9590,7 @@ sriov_params_cb(GError *error, gpointer user_data) if (!nm_platform_link_set_sriov_vfs(nm_device_get_platform(self), priv->ifindex, (const NMPlatformVF *const *) plat_vfs)) { - _LOGE(LOGD_DEVICE, "failed to apply SR-IOV VFs"); - nm_device_state_changed(self, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED); - return; + _LOGW(LOGD_DEVICE, "failed to apply SR-IOV VF configurations"); } priv->stage1_sriov_state = NM_DEVICE_STAGE_STATE_COMPLETED; @@ -9757,10 +9991,12 @@ activate_stage2_device_config(NMDevice *self) nm_device_state_changed(self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); - if (!nm_device_sys_iface_state_is_external_or_assume(self)) + if (!nm_device_sys_iface_state_is_external(self)) { _ethtool_state_set(self); + nm_device_link_properties_set(self, FALSE); + } - if (!nm_device_sys_iface_state_is_external_or_assume(self)) { + if (!nm_device_sys_iface_state_is_external(self)) { if (!priv->tc_committed && !tc_commit(self)) { _LOGW(LOGD_DEVICE, "failed applying traffic control rules"); nm_device_state_changed(self, @@ -10161,14 +10397,6 @@ _dev_ipmanual_check_ready(NMDevice *self) _dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_FAILED); _dev_ip_state_check_async(self, AF_UNSPEC); } else if (ready) { - if (priv->ipmanual_data.state_x[IS_IPv4] != NM_DEVICE_IP_STATE_READY - && nm_l3cfg_has_temp_not_available_obj(priv->l3cfg, addr_family)) { - /* Addresses with pending ACD/DAD are a possible cause for the - * presence of temporarily-not-available objects. Once all addresses - * are ready, retry to commit those unavailable objects. */ - _dev_l3_cfg_commit(self, FALSE); - } - _dev_ipmanual_set_state(self, addr_family, NM_DEVICE_IP_STATE_READY); _dev_ip_state_check_async(self, AF_UNSPEC); } @@ -10334,7 +10562,6 @@ _dev_ipdhcpx_notify(NMDhcpClient *client, const NMDhcpClientNotifyData *notify_d _dev_l3_register_l3cds_set_one_full(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), notify_data->lease_update.l3cd, - NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE, FALSE); if (notify_data->lease_update.accepted) { @@ -10471,6 +10698,7 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) .addr_family = AF_INET, .l3cfg = nm_device_get_l3cfg(self), .iface = nm_device_get_ip_iface(self), + .iface_type_log = nm_device_get_type_desc_for_log(self), .uuid = nm_connection_get_uuid(connection), .hwaddr = hwaddr, .bcast_hwaddr = bcast_hwaddr, @@ -10499,6 +10727,7 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) gboolean iaid_explicit; guint32 iaid; NMDhcpClientConfig config; + const char *pd_hint; iaid = _prop_get_ipvx_dhcp_iaid(self, AF_INET6, connection, FALSE, &iaid_explicit); duid = _prop_get_ipv6_dhcp_duid(self, connection, hwaddr, &enforce_duid); @@ -10507,6 +10736,7 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) .addr_family = AF_INET6, .l3cfg = nm_device_get_l3cfg(self), .iface = nm_device_get_ip_iface(self), + .iface_type_log = nm_device_get_type_desc_for_log(self), .uuid = nm_connection_get_uuid(connection), .send_hostname = nm_setting_ip_config_get_dhcp_send_hostname(s_ip), .hostname = nm_setting_ip_config_get_dhcp_hostname(s_ip), @@ -10525,6 +10755,21 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) }, }; + pd_hint = nm_setting_ip6_config_get_dhcp_pd_hint(NM_SETTING_IP6_CONFIG(s_ip)); + if (pd_hint) { + int pd_hint_length; + gboolean res; + + res = nm_inet_parse_with_prefix_bin(AF_INET6, + pd_hint, + NULL, + &config.v6.pd_hint_addr, + &pd_hint_length); + nm_assert(res); + nm_assert(pd_hint_length > 0 && pd_hint_length <= 128); + config.v6.pd_hint_length = pd_hint_length; + } + priv->ipdhcp_data_6.client = nm_dhcp_manager_start_client(nm_dhcp_manager_get(), &config, &error); } @@ -10555,7 +10800,6 @@ _dev_ipdhcpx_start(NMDevice *self, int addr_family) _dev_l3_register_l3cds_set_one_full(self, L3_CONFIG_DATA_TYPE_DHCP_X(IS_IPv4), previous_lease, - NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE, FALSE); } @@ -10680,10 +10924,13 @@ connection_ip_method_requires_carrier(NMConnection *connection, static gboolean connection_requires_carrier(NMConnection *connection) { - NMSettingIPConfig *s_ip4, *s_ip6; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; NMSettingConnection *s_con; - gboolean ip4_carrier_wanted, ip6_carrier_wanted; - gboolean ip4_used = FALSE, ip6_used = FALSE; + gboolean ip4_carrier_wanted; + gboolean ip6_carrier_wanted; + gboolean ip4_used = FALSE; + gboolean ip6_used = FALSE; /* We can progress to IP_CONFIG now, so that we're enslaved. * That may actually cause carrier to go up and thus continue activation. */ @@ -11196,10 +11443,8 @@ _commit_mtu(NMDevice *self) if (ifindex <= 0) return; - if (!nm_device_get_applied_connection(self) - || nm_device_sys_iface_state_is_external_or_assume(self)) { - /* we don't tamper with the MTU of disconnected and - * external/assumed devices. */ + if (!nm_device_get_applied_connection(self) || nm_device_sys_iface_state_is_external(self)) { + /* we don't tamper with the MTU of disconnected and external devices. */ return; } @@ -11378,8 +11623,8 @@ _commit_mtu(NMDevice *self) ? "Are the MTU sizes of the slaves large enough?" : "Did you configure the MTU correctly?")); } - priv->carrier_wait_until_ms = - nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MS; + priv->carrier_wait_until_msec = + nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MSEC; } if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS()) { @@ -11408,8 +11653,8 @@ _commit_mtu(NMDevice *self) msg ? ": " : "", msg ?: ""); } - priv->carrier_wait_until_ms = - nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MS; + priv->carrier_wait_until_msec = + nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MSEC; } } @@ -11488,11 +11733,7 @@ _dev_ipac6_ndisc_config_changed(NMNDisc *ndisc, _dev_ipac6_grace_period_start(self, 0, TRUE); - _dev_l3_register_l3cds_set_one_full(self, - L3_CONFIG_DATA_TYPE_AC_6, - l3cd, - NM_L3CFG_CONFIG_FLAGS_FORCE_ONCE, - FALSE); + _dev_l3_register_l3cds_set_one_full(self, L3_CONFIG_DATA_TYPE_AC_6, l3cd, FALSE); nm_clear_l3cd(&priv->ipac6_data.l3cd); ready = nm_l3cfg_check_ready(priv->l3cfg, @@ -11682,7 +11923,11 @@ _dev_ipac6_start(NMDevice *self) } if (nm_device_get_ip_iface_identifier(self, &iid, FALSE, &is_token)) { - _LOGD_ipac6("using the device EUI-64 identifier"); + char buf[INET6_ADDRSTRLEN]; + + _LOGD_ipac6("using the device EUI-64 identifier %s (from %s)", + nm_utils_inet6_interface_identifier_to_token(&iid, buf), + is_token ? "token" : "address"); nm_ndisc_set_iid(priv->ipac6_data.ndisc, iid, is_token); } else { /* Don't abort the addrconf at this point -- if ndisc needs the iid @@ -12610,24 +12855,24 @@ nm_device_is_nm_owned(NMDevice *self) static gboolean delete_on_deactivate_link_delete(gpointer user_data) { - DeleteOnDeactivateData *data = user_data; - nm_auto_unref_object NMDevice *self = data->device; + nm_auto_unref_object NMDevice *self = user_data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); gs_free_error GError *error = NULL; - _LOGD(LOGD_DEVICE, - "delete_on_deactivate: cleanup and delete virtual link (id=%u)", - data->idle_add_id); + _LOGD(LOGD_DEVICE, "delete_on_deactivate: cleanup and delete virtual link"); - priv->delete_on_deactivate_data = NULL; + nm_clear_g_source_inst(&priv->delete_on_deactivate_idle_source); if (!nm_device_unrealize(self, TRUE, &error)) _LOGD(LOGD_DEVICE, "delete_on_deactivate: unrealizing failed (%s)", error->message); - nm_device_emit_recheck_auto_activate(self); + if (nm_dbus_object_is_exported(NM_DBUS_OBJECT(self))) { + /* The device is still alive. We may need to autoactivate virtual + * devices again. */ + nm_device_recheck_auto_activate_schedule(self); + } - g_free(data); - return FALSE; + return G_SOURCE_CONTINUE; } static void @@ -12635,25 +12880,16 @@ delete_on_deactivate_unschedule(NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - if (priv->delete_on_deactivate_data) { - DeleteOnDeactivateData *data = priv->delete_on_deactivate_data; - - priv->delete_on_deactivate_data = NULL; - - g_source_remove(data->idle_add_id); - _LOGD(LOGD_DEVICE, - "delete_on_deactivate: cancel cleanup and delete virtual link (id=%u)", - data->idle_add_id); - g_object_unref(data->device); - g_free(data); + if (nm_clear_g_source_inst(&priv->delete_on_deactivate_idle_source)) { + _LOGD(LOGD_DEVICE, "delete_on_deactivate: cancel cleanup and delete virtual link"); + g_object_unref(self); } } static void delete_on_deactivate_check_and_schedule(NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - DeleteOnDeactivateData *data; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); if (!priv->nm_owned) return; @@ -12663,18 +12899,13 @@ delete_on_deactivate_check_and_schedule(NMDevice *self) return; if (nm_device_get_state(self) == NM_DEVICE_STATE_UNMANAGED) return; - if (nm_device_get_state(self) == NM_DEVICE_STATE_UNAVAILABLE) - return; - delete_on_deactivate_unschedule(self); /* always cancel and reschedule */ - data = g_new(DeleteOnDeactivateData, 1); - data->device = g_object_ref(self); - data->idle_add_id = g_idle_add(delete_on_deactivate_link_delete, data); - priv->delete_on_deactivate_data = data; + g_object_ref(self); + delete_on_deactivate_unschedule(self); /* always cancel and reschedule */ + priv->delete_on_deactivate_idle_source = + nm_g_idle_add_source(delete_on_deactivate_link_delete, self); - _LOGD(LOGD_DEVICE, - "delete_on_deactivate: schedule cleanup and delete virtual link (id=%u)", - data->idle_add_id); + _LOGD(LOGD_DEVICE, "delete_on_deactivate: schedule cleanup and delete virtual link"); } static void @@ -12835,7 +13066,8 @@ can_reapply_change(NMDevice *self, NM_SETTING_USER_SETTING_NAME, NM_SETTING_PROXY_SETTING_NAME, NM_SETTING_IP4_CONFIG_SETTING_NAME, - NM_SETTING_IP6_CONFIG_SETTING_NAME)) + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_LINK_SETTING_NAME)) return TRUE; if (nm_streq(setting_name, NM_SETTING_WIRED_SETTING_NAME)) { @@ -12884,7 +13116,7 @@ reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new) * Change configuration of an already configured device if possible. * Updates the device's applied connection upon success. * - * Return: %FALSE if the new configuration can not be reapplied. + * Returns: %FALSE if the new configuration can not be reapplied. */ static gboolean check_and_reapply_connection(NMDevice *self, @@ -13032,6 +13264,8 @@ check_and_reapply_connection(NMDevice *self, *************************************************************************/ klass->reapply_connection(self, con_old, con_new); + nm_device_link_properties_set(self, TRUE); + if (priv->state >= NM_DEVICE_STATE_CONFIG) lldp_setup(self, NM_TERNARY_DEFAULT); @@ -13079,7 +13313,7 @@ check_and_reapply_connection(NMDevice *self, if (sett_conn) { nm_settings_connection_autoconnect_blocked_reason_set( sett_conn, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, + NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_USER_REQUEST, FALSE); } @@ -13414,7 +13648,8 @@ delete_cb(NMDevice *self, GError *error, gpointer user_data) { - GError *local = NULL; + NMSettingsConnection *sett_conn; + GError *local = NULL; if (error) { g_dbus_method_invocation_return_gerror(context, error); @@ -13429,10 +13664,26 @@ delete_cb(NMDevice *self, /* Authorized */ nm_audit_log_device_op(NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, NULL, subject, NULL); - if (nm_device_unrealize(self, TRUE, &local)) - g_dbus_method_invocation_return_value(context, NULL); - else + + sett_conn = nm_device_get_settings_connection(self); + if (sett_conn) { + /* Block profile from autoconnecting. We block the profile, which may + * be ugly/wrong with multi-connect profiles. However, it's not + * obviously wrong, because profiles for software devices tend not to + * work with multi-connect anyway, because they describe a (unique) + * interface by name. */ + nm_settings_connection_autoconnect_blocked_reason_set( + sett_conn, + NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_USER_REQUEST, + TRUE); + } + + if (!nm_device_unrealize(self, TRUE, &local)) { g_dbus_method_invocation_take_error(context, local); + return; + } + + g_dbus_method_invocation_return_value(context, NULL); } static void @@ -13539,7 +13790,7 @@ _carrier_wait_check_act_request_must_queue(NMDevice *self, NMActRequest *req) * request is not blocked waiting for carrier. */ if (priv->carrier) return FALSE; - if (priv->carrier_wait_id == 0) + if (!priv->carrier_wait_source) return FALSE; connection = nm_act_request_get_applied_connection(req); @@ -14053,11 +14304,11 @@ carrier_wait_timeout(gpointer user_data) NMDevice *self = NM_DEVICE(user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - priv->carrier_wait_id = 0; + nm_clear_g_source_inst(&priv->carrier_wait_source); nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); if (!priv->carrier) _carrier_wait_check_queued_act_request(self); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } static gboolean @@ -14074,14 +14325,15 @@ nm_device_is_up(NMDevice *self) static gint64 _get_carrier_wait_ms(NMDevice *self) { - return nm_config_data_get_device_config_int64(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, - self, - 10, - 0, - G_MAXINT32, - CARRIER_WAIT_TIME_MS, - CARRIER_WAIT_TIME_MS); + return nm_config_data_get_device_config_int64_by_device( + NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT, + self, + 10, + 0, + G_MAXINT32, + CARRIER_WAIT_TIME_MS, + CARRIER_WAIT_TIME_MS); } /* @@ -14104,13 +14356,14 @@ carrier_detect_wait(NMDevice *self) * * If during that time carrier goes away, we declare the interface * as not ready. */ - nm_clear_g_source(&priv->carrier_wait_id); + nm_clear_g_source_inst(&priv->carrier_wait_source); if (!priv->carrier) nm_device_add_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); now_ms = nm_utils_get_monotonic_timestamp_msec(); - until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_ms); - priv->carrier_wait_id = g_timeout_add(until_ms - now_ms, carrier_wait_timeout, self); + until_ms = NM_MAX(now_ms + _get_carrier_wait_ms(self), priv->carrier_wait_until_msec); + priv->carrier_wait_source = + nm_g_timeout_add_source(until_ms - now_ms, carrier_wait_timeout, self); } gboolean @@ -14563,7 +14816,15 @@ _set_unmanaged_flags(NMDevice *self, new_state = was_managed ? NM_DEVICE_STATE_UNMANAGED : NM_DEVICE_STATE_UNAVAILABLE; if (new_state == NM_DEVICE_STATE_UNMANAGED) { _cancel_activation(self); + } else { + /* The assume check should happen before the device transitions to + * UNAVAILABLE, because in UNAVAILABLE we already clean up the IP + * configuration. Therefore, this function should never trigger a + * sync state transition. + */ + nm_device_queue_recheck_assume(self); } + if (now) nm_device_state_changed(self, new_state, reason); else @@ -14629,11 +14890,11 @@ nm_device_check_unrealized_device_managed(NMDevice *self) nm_assert(!nm_device_is_real(self)); - if (!nm_config_data_get_device_config_boolean(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, - self, - TRUE, - TRUE)) + if (!nm_config_data_get_device_config_boolean_by_device(NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + self, + TRUE, + TRUE)) return FALSE; if (nm_device_spec_match_list(self, nm_settings_get_unmanaged_specs(priv->settings))) @@ -14700,11 +14961,11 @@ nm_device_set_unmanaged_by_user_conf(NMDevice *self) gboolean value; NMUnmanFlagOp set_op; - value = nm_config_data_get_device_config_boolean(NM_CONFIG_GET_DATA, - NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, - self, - -1, - TRUE); + value = nm_config_data_get_device_config_boolean_by_device(NM_CONFIG_GET_DATA, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + self, + -1, + TRUE); switch (value) { case TRUE: set_op = NM_UNMAN_FLAG_OP_SET_MANAGED; @@ -14739,7 +15000,7 @@ nm_device_set_unmanaged_by_quitting(NMDevice *self) nm_device_set_unmanaged_by_flags(self, NM_UNMANAGED_QUITTING, - TRUE, + NM_UNMAN_FLAG_OP_SET_UNMANAGED, need_deactivate ? NM_DEVICE_STATE_REASON_REMOVED : NM_DEVICE_STATE_REASON_NOW_UNMANAGED); } @@ -14918,7 +15179,10 @@ _nm_device_check_connection_available(NMDevice *self, /* an unrealized software device is always available, hardware devices never. */ if (!nm_device_is_real(self)) { if (nm_device_is_software(self)) { - if (!nm_device_check_connection_compatible(self, connection, error ? &local : NULL)) { + if (!nm_device_check_connection_compatible(self, + connection, + TRUE, + error ? &local : NULL)) { if (error) { g_return_val_if_fail(local, FALSE); nm_utils_error_set(error, @@ -14982,7 +15246,7 @@ _nm_device_check_connection_available(NMDevice *self, } } - if (!nm_device_check_connection_compatible(self, connection, error ? &local : NULL)) { + if (!nm_device_check_connection_compatible(self, connection, TRUE, error ? &local : NULL)) { if (error) { nm_utils_error_set(error, local->domain == NM_UTILS_ERROR ? local->code @@ -15084,14 +15348,11 @@ check_connection_available(NMDevice *self, { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); - /* Connections which require a network connection are not available when - * the device has no carrier, even with ignore-carrer=TRUE. - */ - if (priv->carrier || !connection_requires_carrier(connection)) + if (priv->carrier) return TRUE; if (NM_FLAGS_HAS(flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER) - && priv->carrier_wait_id != 0) { + && priv->carrier_wait_source) { /* The device has no carrier though the connection requires it. * * If we are still waiting for carrier, the connection is available @@ -15099,12 +15360,6 @@ check_connection_available(NMDevice *self, return TRUE; } - /* master types are always available even without carrier. - * Making connection non-available would un-enslave slaves which - * is not desired. */ - if (nm_device_is_master(self)) - return TRUE; - if (!priv->up) { /* If the device is !IFF_UP it also has no carrier. But we assume that if we * would start activating the device (and thereby set the device IFF_UP), @@ -15114,6 +15369,18 @@ check_connection_available(NMDevice *self, return TRUE; } + if (!connection_requires_carrier(connection)) { + /* Connections that don't require carrier are available. */ + return TRUE; + } + + if (nm_device_is_master(self)) { + /* master types are always available even without carrier. + * Making connection non-available would un-enslave slaves which + * is not desired. */ + return TRUE; + } + nm_utils_error_set_literal(error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "device has no carrier"); @@ -15479,7 +15746,7 @@ _cleanup_generic_pre(NMDevice *self, CleanupType cleanup_type) } static void -_cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) +_cleanup_generic_post(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanup_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); @@ -15502,7 +15769,11 @@ _cleanup_generic_post(NMDevice *self, CleanupType cleanup_type) act_request_set(self, NULL); } - if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE + && ((reason == NM_DEVICE_STATE_REASON_CARRIER && nm_device_is_master(self)) + || !NM_IN_SET(reason, + NM_DEVICE_STATE_REASON_NOW_MANAGED, + NM_DEVICE_STATE_REASON_CARRIER))) { /* Check if the device was deactivated, and if so, delete_link. * Don't call delete_link synchronously because we are currently * handling a state change -- which is not reentrant. */ @@ -15623,8 +15894,8 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu ifindex); if (priv->mtu_initial) { nm_platform_link_set_mtu(nm_device_get_platform(self), ifindex, priv->mtu_initial); - priv->carrier_wait_until_ms = - nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MS; + priv->carrier_wait_until_msec = + nm_utils_get_monotonic_timestamp_msec() + CARRIER_WAIT_TIME_AFTER_MTU_MSEC; } if (priv->ip6_mtu_initial) { char sbuf[64]; @@ -15641,6 +15912,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu } _ethtool_state_reset(self); + link_properties_reset(self); if (priv->promisc_reset != NM_OPTION_BOOL_DEFAULT && ifindex > 0) { nm_platform_link_change_flags(nm_device_get_platform(self), @@ -15650,7 +15922,7 @@ nm_device_cleanup(NMDevice *self, NMDeviceStateReason reason, CleanupType cleanu priv->promisc_reset = NM_OPTION_BOOL_DEFAULT; } - _cleanup_generic_post(self, cleanup_type); + _cleanup_generic_post(self, reason, cleanup_type); } static void @@ -15675,6 +15947,9 @@ deactivate_ready(NMDevice *self, NMDeviceStateReason reason) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + if (priv->port_detach_count > 0) + return; + if (priv->dispatcher.call_id) return; @@ -15966,8 +16241,11 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, * userspace IPv6LL enabled. */ _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_NONE); + if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_REMOVED) { + nm_device_cleanup(self, reason, CLEANUP_TYPE_REMOVED); + } else + nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); - nm_device_cleanup(self, reason, CLEANUP_TYPE_DECONFIGURE); } else if (old_state < NM_DEVICE_STATE_DISCONNECTED) { if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) { /* Ensure IPv6 is set up as it may not have been done when @@ -16036,7 +16314,8 @@ _set_state_full(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason, /* We cache the ignore_carrier state to not react on config-reloads while the connection * is active. But on deactivating, reset the ignore-carrier flag to the current state. */ - priv->ignore_carrier = nm_config_data_get_ignore_carrier(NM_CONFIG_GET_DATA, self); + priv->ignore_carrier = + nm_config_data_get_ignore_carrier_by_device(NM_CONFIG_GET_DATA, self); if (quitting) { nm_dispatcher_call_device_sync(NM_DISPATCHER_ACTION_PRE_DOWN, self, req); @@ -16743,7 +17022,7 @@ nm_device_hw_addr_set(NMDevice *self, const char *addr, const char *detail, gboo * @hwaddr: (out): the cloned MAC address to set on interface * @hwaddr_type: (out): the type of address to set * @hwaddr_detail: (out): the detail (origin) of address to set - * @error: (out): on return, an error or %NULL + * @error: on return, an error or %NULL * * Computes the MAC to be set on a interface. On success, one of the * following exclusive conditions are verified: @@ -16940,6 +17219,7 @@ nm_device_hw_addr_reset(NMDevice *self, const char *detail) { NMDevicePrivate *priv; const char *addr; + int ifindex; g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); @@ -16949,7 +17229,13 @@ nm_device_hw_addr_reset(NMDevice *self, const char *detail) return TRUE; priv->hw_addr_type = HW_ADDR_TYPE_UNSET; - addr = nm_device_get_initial_hw_address(self); + + ifindex = nm_device_get_ip_ifindex(self); + if (ifindex <= 0) { + return TRUE; + } + + addr = nm_device_get_initial_hw_address(self); if (!addr) { /* as hw_addr_type is not UNSET, we expect that we can get an * initial address to which to reset. */ @@ -17028,38 +17314,11 @@ nm_device_spec_match_list(NMDevice *self, const GSList *specs) int nm_device_spec_match_list_full(NMDevice *self, const GSList *specs, int no_match_value) { - NMDeviceClass *klass; - NMMatchSpecMatchType m; - const char *hw_address = NULL; - gboolean is_fake; - - g_return_val_if_fail(NM_IS_DEVICE(self), FALSE); + NMMatchSpecDeviceData data; + NMMatchSpecMatchType m; - klass = NM_DEVICE_GET_CLASS(self); - hw_address = nm_device_get_permanent_hw_address_full( - self, - !nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT), - &is_fake); - - m = nm_match_spec_device(specs, - nm_device_get_iface(self), - nm_device_get_type_description(self), - nm_device_get_driver(self), - nm_device_get_driver_version(self), - is_fake ? NULL : hw_address, - klass->get_s390_subchannels ? klass->get_s390_subchannels(self) : NULL, - nm_dhcp_manager_get_config(nm_dhcp_manager_get())); - - switch (m) { - case NM_MATCH_SPEC_MATCH: - return TRUE; - case NM_MATCH_SPEC_NEG_MATCH: - return FALSE; - case NM_MATCH_SPEC_NO_MATCH: - return no_match_value; - } - nm_assert_not_reached(); - return no_match_value; + m = nm_match_spec_device(specs, nm_match_spec_device_data_init_from_device(&data, self)); + return nm_match_spec_match_type_to_bool(m, no_match_value); } guint @@ -17747,6 +18006,8 @@ nm_device_init(NMDevice *self) c_list_init(&priv->concheck_lst_head); c_list_init(&self->devices_lst); + c_list_init(&self->devcon_dev_lst_head); + c_list_init(&self->policy_auto_activate_lst); c_list_init(&priv->slaves); priv->ipdhcp_data_6.v6.mode = NM_NDISC_DHCP_LEVEL_NONE; @@ -17773,7 +18034,11 @@ nm_device_init(NMDevice *self) priv->unmanaged_mask = priv->unmanaged_flags; 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); - priv->sys_iface_state_ = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; + + priv->sys_iface_state_ = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; + /* If networking is already disabled at boot, we want to manage all devices + * after re-enabling networking; hence, the initial state is MANAGED. */ + priv->sys_iface_state_before_sleep = NM_DEVICE_SYS_IFACE_STATE_MANAGED; priv->promisc_reset = NM_OPTION_BOOL_DEFAULT; } @@ -17867,6 +18132,9 @@ dispose(GObject *object) _LOGD(LOGD_DEVICE, "disposing"); nm_assert(c_list_is_empty(&self->devices_lst)); + nm_assert(c_list_is_empty(&self->devcon_dev_lst_head)); + nm_assert(c_list_is_empty(&self->policy_auto_activate_lst)); + nm_assert(!self->policy_auto_activate_idle_source); while ((con_handle = c_list_first_entry(&priv->concheck_lst_head, NMDeviceConnectivityHandle, @@ -17899,7 +18167,7 @@ dispose(GObject *object) /* Let the kernel manage IPv6LL again */ _dev_addrgenmode6_set(self, NM_IN6_ADDR_GEN_MODE_EUI64); - _cleanup_generic_post(self, CLEANUP_TYPE_KEEP); + _cleanup_generic_post(self, NM_DEVICE_STATE_REASON_NONE, CLEANUP_TYPE_KEEP); nm_assert(priv->master_ready_id == 0); @@ -17925,7 +18193,7 @@ dispose(GObject *object) available_connections_del_all(self); - if (nm_clear_g_source(&priv->carrier_wait_id)) + if (nm_clear_g_source_inst(&priv->carrier_wait_source)) nm_device_remove_pending_action(self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); _clear_queued_act_request(priv, NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); @@ -18529,16 +18797,6 @@ nm_device_class_init(NMDeviceClass *klass) G_TYPE_NONE, 0); - signals[RECHECK_AUTO_ACTIVATE] = g_signal_new(NM_DEVICE_RECHECK_AUTO_ACTIVATE, - G_OBJECT_CLASS_TYPE(object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 0); - signals[RECHECK_ASSUME] = g_signal_new(NM_DEVICE_RECHECK_ASSUME, G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_FIRST, diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index bcf4d7b9..b096d23a 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -75,7 +75,6 @@ #define NM_DEVICE_IP6_PREFIX_DELEGATED "ip6-prefix-delegated" #define NM_DEVICE_IP6_SUBNET_NEEDED "ip6-subnet-needed" #define NM_DEVICE_REMOVED "removed" -#define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" #define NM_DEVICE_RECHECK_ASSUME "recheck-assume" #define NM_DEVICE_STATE_CHANGED "state-changed" #define NM_DEVICE_LINK_INITIALIZED "link-initialized" @@ -144,6 +143,10 @@ struct _NMDevice { NMDBusObject parent; struct _NMDevicePrivate *_priv; CList devices_lst; + CList devcon_dev_lst_head; + + CList policy_auto_activate_lst; + GSource *policy_auto_activate_idle_source; }; /* The flags have an relaxing meaning, that means, specifying more flags, can make @@ -294,7 +297,7 @@ typedef struct _NMDeviceClass { GPtrArray *(*get_extra_rules)(NMDevice *self); /* allow derived classes to override the result of nm_device_autoconnect_allowed(). - * If the value changes, the class should call nm_device_emit_recheck_auto_activate(), + * If the value changes, the class should call nm_device_recheck_auto_activate_schedule(), * which emits NM_DEVICE_RECHECK_AUTO_ACTIVATE signal. */ gboolean (*get_autoconnect_allowed)(NMDevice *self); @@ -321,6 +324,7 @@ typedef struct _NMDeviceClass { */ gboolean (*check_connection_compatible)(NMDevice *self, NMConnection *connection, + gboolean check_properties, GError **error); /* Checks whether the connection is likely available to be activated, @@ -387,7 +391,15 @@ typedef struct _NMDeviceClass { GCancellable *cancellable, NMDeviceAttachPortCallback callback, gpointer user_data); - void (*detach_port)(NMDevice *self, NMDevice *port, gboolean configure); + /* This works similarly to attach_port(). However, current + * implementations don't report errors and so the only possible + * return values are TRUE and DEFAULT. */ + NMTernary (*detach_port)(NMDevice *self, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data); void (*parent_changed_notify)(NMDevice *self, int old_ifindex, @@ -421,6 +433,10 @@ typedef struct _NMDeviceClass { const char *(*get_dhcp_anycast_address)(NMDevice *self); } NMDeviceClass; +NMSettings *nm_device_get_settings(NMDevice *self); + +NMManager *nm_device_get_manager(NMDevice *self); + GType nm_device_get_type(void); struct _NMDedupMultiIndex *nm_device_get_multi_index(NMDevice *self); @@ -444,9 +460,11 @@ gboolean nm_device_is_real(NMDevice *dev); const char *nm_device_get_ip_iface(NMDevice *dev); const char *nm_device_get_ip_iface_from_platform(NMDevice *dev); int nm_device_get_ip_ifindex(const NMDevice *dev); +const char *nm_device_get_s390_subchannels(NMDevice *self); const char *nm_device_get_driver(NMDevice *dev); const char *nm_device_get_driver_version(NMDevice *dev); const char *nm_device_get_type_desc(NMDevice *dev); +const char *nm_device_get_type_desc_for_log(NMDevice *dev); const char *nm_device_get_type_description(NMDevice *dev); NMDeviceType nm_device_get_device_type(NMDevice *dev); NMLinkType nm_device_get_link_type(NMDevice *dev); @@ -526,8 +544,10 @@ gboolean nm_device_complete_connection(NMDevice *device, NMConnection *const *existing_connections, GError **error); -gboolean -nm_device_check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error); +gboolean nm_device_check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error); gboolean nm_device_check_slave_connection_compatible(NMDevice *device, NMConnection *connection); @@ -608,9 +628,9 @@ typedef enum { } NMUnmanagedFlags; typedef enum { - NM_UNMAN_FLAG_OP_SET_MANAGED = FALSE, - NM_UNMAN_FLAG_OP_SET_UNMANAGED = TRUE, - NM_UNMAN_FLAG_OP_FORGET = 2, + NM_UNMAN_FLAG_OP_SET_MANAGED = 0, + NM_UNMAN_FLAG_OP_SET_UNMANAGED, + NM_UNMAN_FLAG_OP_FORGET, } NMUnmanFlagOp; const char *nm_unmanaged_flags2str(NMUnmanagedFlags flags, char *buf, gsize len); @@ -631,6 +651,7 @@ void nm_device_set_unmanaged_by_user_settings(NMDevice *self, gboolean now); void nm_device_set_unmanaged_by_user_udev(NMDevice *self); void nm_device_set_unmanaged_by_user_conf(NMDevice *self); void nm_device_set_unmanaged_by_quitting(NMDevice *device); +NMDeviceStateReason nm_device_get_manage_reason_external(NMDevice *self); gboolean nm_device_check_unrealized_device_managed(NMDevice *self); @@ -701,7 +722,7 @@ nm_device_autoconnect_blocked_unset(NMDevice *device, NMDeviceAutoconnectBlocked nm_device_autoconnect_blocked_set_full(device, mask, NM_DEVICE_AUTOCONNECT_BLOCKED_NONE); } -void nm_device_emit_recheck_auto_activate(NMDevice *device); +void nm_device_recheck_auto_activate_schedule(NMDevice *device); NMDeviceSysIfaceState nm_device_sys_iface_state_get(NMDevice *device); @@ -710,6 +731,10 @@ gboolean nm_device_sys_iface_state_is_external_or_assume(NMDevice *self); void nm_device_sys_iface_state_set(NMDevice *device, NMDeviceSysIfaceState sys_iface_state); +void nm_device_notify_sleeping(NMDevice *self); + +NMDeviceSysIfaceState nm_device_get_sys_iface_state_before_sleep(NMDevice *self); + void nm_device_state_changed(NMDevice *device, NMDeviceState state, NMDeviceStateReason reason); void nm_device_queue_state(NMDevice *self, NMDeviceState state, NMDeviceStateReason reason); diff --git a/src/core/devices/ovs/nm-device-ovs-bridge.c b/src/core/devices/ovs/nm-device-ovs-bridge.c index 7b319af3..ff1917b1 100644 --- a/src/core/devices/ovs/nm-device-ovs-bridge.c +++ b/src/core/devices/ovs/nm-device-ovs-bridge.c @@ -97,9 +97,16 @@ attach_port(NMDevice *device, return TRUE; } -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) -{} +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) +{ + return TRUE; +} void nm_device_ovs_reapply_connection(NMDevice *self, NMConnection *con_old, NMConnection *con_new) diff --git a/src/core/devices/ovs/nm-device-ovs-interface.c b/src/core/devices/ovs/nm-device-ovs-interface.c index 711f65cb..fd48c2fd 100644 --- a/src/core/devices/ovs/nm-device-ovs-interface.c +++ b/src/core/devices/ovs/nm-device-ovs-interface.c @@ -91,12 +91,15 @@ can_auto_connect(NMDevice *device, NMSettingsConnection *sett_conn, char **speci } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingOvsInterface *s_ovs_iface; if (!NM_DEVICE_CLASS(nm_device_ovs_interface_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_ovs_iface = nm_connection_get_setting_ovs_interface(connection); @@ -132,6 +135,8 @@ link_changed(NMDevice *device, const NMPlatformLink *pllink) nm_device_devip_set_failed(device, AF_INET6, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; } + + nm_device_link_properties_set(device, FALSE); nm_device_bring_up(device); nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL); @@ -214,6 +219,7 @@ _set_ip_ifindex_tun(gpointer user_data) priv->wait_link_is_waiting = FALSE; nm_device_set_ip_ifindex(device, priv->wait_link_ifindex); + nm_device_link_properties_set(device, FALSE); nm_device_devip_set_state(device, AF_INET, NM_DEVICE_IP_STATE_PENDING, NULL); nm_device_devip_set_state(device, AF_INET6, NM_DEVICE_IP_STATE_PENDING, NULL); @@ -303,6 +309,7 @@ act_stage3_ip_config(NMDevice *device, int addr_family) return; } + nm_device_link_properties_set(device, FALSE); nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, NULL); } @@ -479,7 +486,7 @@ ovsdb_ready(NMOvsdb *ovsdb, NMDeviceOvsInterface *self) NM_DEVICE_STATE_REASON_NONE, NM_DEVICE_STATE_REASON_NONE); nm_device_recheck_available_connections(device); - nm_device_emit_recheck_auto_activate(device); + nm_device_recheck_auto_activate_schedule(device); } static void diff --git a/src/core/devices/ovs/nm-device-ovs-port.c b/src/core/devices/ovs/nm-device-ovs-port.c index 5510e39f..5ede46e9 100644 --- a/src/core/devices/ovs/nm-device-ovs-port.c +++ b/src/core/devices/ovs/nm-device-ovs-port.c @@ -78,10 +78,11 @@ typedef struct { GCancellable *cancellable; NMDeviceAttachPortCallback callback; gpointer callback_user_data; + gboolean add; } AttachPortData; static void -add_iface_cb(GError *error, gpointer user_data) +add_del_iface_cb(GError *error, gpointer user_data) { AttachPortData *data = user_data; NMDeviceOvsPort *self; @@ -93,15 +94,17 @@ add_iface_cb(GError *error, gpointer user_data) } else if (error && !nm_utils_error_is_cancelled_or_disposing(error)) { self = NM_DEVICE_OVS_PORT(data->device); _LOGW(LOGD_DEVICE, - "device %s could not be added to a ovs port: %s", + "device %s could not be %s a ovs port: %s", nm_device_get_iface(data->port), + data->add ? "added to" : "removed from", error->message); nm_device_state_changed(data->port, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_OVSDB_FAILED); } - data->callback(data->device, error, data->callback_user_data); + if (data->callback) + data->callback(data->device, error, data->callback_user_data); g_object_unref(data->device); g_object_unref(data->port); @@ -178,6 +181,7 @@ attach_port(NMDevice *device, .cancellable = g_object_ref(cancellable), .callback = callback, .callback_user_data = user_data, + .add = TRUE, }; nm_ovsdb_add_interface(nm_ovsdb_get(), @@ -186,7 +190,7 @@ attach_port(NMDevice *device, nm_device_get_applied_connection(port), bridge_device, port, - add_iface_cb, + add_del_iface_cb, data); /* DPDK ports does not have a link after the devbind, so the MTU must be @@ -205,29 +209,19 @@ attach_port(NMDevice *device, return NM_TERNARY_DEFAULT; } -static void -del_iface_cb(GError *error, gpointer user_data) -{ - NMDevice *slave = user_data; - - if (error && !g_error_matches(error, NM_UTILS_ERROR, NM_UTILS_ERROR_CANCELLED_DISPOSING)) { - nm_log_warn(LOGD_DEVICE, - "device %s could not be removed from a ovs port: %s", - nm_device_get_iface(slave), - error->message); - nm_device_state_changed(slave, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_OVSDB_FAILED); - } - - g_object_unref(slave); -} - -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceOvsPort *self = NM_DEVICE_OVS_PORT(device); bool port_not_managed = !NM_IN_SET(nm_device_sys_iface_state_get(port), NM_DEVICE_SYS_IFACE_STATE_MANAGED, NM_DEVICE_SYS_IFACE_STATE_ASSUME); + NMTernary ret = TRUE; _LOGI(LOGD_DEVICE, "detaching ovs interface %s", nm_device_get_ip_iface(port)); @@ -236,10 +230,20 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) * to make sure its OVSDB entry is gone. */ if (configure || port_not_managed) { - nm_ovsdb_del_interface(nm_ovsdb_get(), - nm_device_get_iface(port), - del_iface_cb, - g_object_ref(port)); + AttachPortData *data; + + data = g_slice_new(AttachPortData); + *data = (AttachPortData){ + .device = g_object_ref(device), + .port = g_object_ref(port), + .cancellable = nm_g_object_ref(cancellable), + .callback = callback, + .callback_user_data = user_data, + .add = FALSE, + }; + + nm_ovsdb_del_interface(nm_ovsdb_get(), nm_device_get_iface(port), add_del_iface_cb, data); + ret = NM_TERNARY_DEFAULT; } if (configure) { @@ -248,6 +252,8 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) if (NM_IS_DEVICE_OVS_INTERFACE(port)) nm_device_update_from_platform_link(port, NULL); } + + return ret; } /*****************************************************************************/ diff --git a/src/core/devices/ovs/nm-ovs-factory.c b/src/core/devices/ovs/nm-ovs-factory.c index 50023778..2ca1a0b5 100644 --- a/src/core/devices/ovs/nm-ovs-factory.c +++ b/src/core/devices/ovs/nm-ovs-factory.c @@ -240,9 +240,11 @@ ovsdb_interface_failed(NMOvsdb *ovsdb, return; if (connection) { - nm_settings_connection_autoconnect_blocked_reason_set( + nm_manager_devcon_autoconnect_blocked_reason_set( + nm_device_get_manager(device), + device, connection, - NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, + NM_SETTINGS_AUTOCONNECT_BLOCKED_REASON_FAILED, TRUE); } diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index 85b7953f..68366f73 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -12,6 +12,7 @@ #include "libnm-glib-aux/nm-jansson.h" #include "libnm-glib-aux/nm-str-buf.h" +#include "libnm-glib-aux/nm-io-utils.h" #include "nm-core-utils.h" #include "libnm-core-intern/nm-core-internal.h" #include "devices/nm-device.h" @@ -134,14 +135,18 @@ enum { static guint signals[LAST_SIGNAL] = {0}; typedef struct { - NMPlatform *platform; - GSocketConnection *conn; - GCancellable *conn_cancellable; - char buf[4096]; /* Input buffer */ - size_t bufp; /* Last decoded byte in the input buffer. */ - GString *input; /* JSON stream waiting for decoding. */ - GString *output; /* JSON stream to be sent. */ - guint64 call_id_counter; + NMPlatform *platform; + int conn_fd; + GSource *conn_fd_in_source; + GSource *conn_fd_out_source; + GCancellable *conn_cancellable; + + NMStrBuf input_buf; + NMStrBuf output_buf; + + GSource *input_timeout_source; + + guint64 call_id_counter; CList calls_lst_head; @@ -177,12 +182,13 @@ NM_DEFINE_SINGLETON_GETTER(NMOvsdb, nm_ovsdb_get, NM_TYPE_OVSDB); /*****************************************************************************/ -static void ovsdb_try_connect(NMOvsdb *self); -static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing); -static void ovsdb_read(NMOvsdb *self); -static void ovsdb_write(NMOvsdb *self); -static void ovsdb_next_command(NMOvsdb *self); -static void cleanup_check_ready(NMOvsdb *self); +static void ovsdb_try_connect(NMOvsdb *self); +static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing); +static void ovsdb_read(NMOvsdb *self); +static void ovsdb_write_try(NMOvsdb *self); +static gboolean ovsdb_write_cb(int fd, GIOCondition condition, gpointer user_data); +static void ovsdb_next_command(NMOvsdb *self); +static void cleanup_check_ready(NMOvsdb *self); /*****************************************************************************/ @@ -1446,10 +1452,10 @@ ovsdb_next_command(NMOvsdb *self) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); OvsdbMethodCall *call; - char *cmd; + nm_auto_free char *cmd = NULL; nm_auto_decref_json json_t *msg = NULL; - if (!priv->conn) + if (priv->conn_fd < 0) return; if (c_list_is_empty(&priv->calls_lst_head)) @@ -1586,10 +1592,9 @@ ovsdb_next_command(NMOvsdb *self) cmd = json_dumps(msg, 0); _LOGT_call(call, "send: call-id=%" G_GUINT64_FORMAT ", %s", call->call_id, cmd); - g_string_append(priv->output, cmd); - free(cmd); + nm_str_buf_append(&priv->output_buf, cmd); - ovsdb_write(self); + ovsdb_write_try(self); } /** @@ -2188,20 +2193,18 @@ ovsdb_got_update(NMOvsdb *self, json_t *msg) static void ovsdb_got_echo(NMOvsdb *self, json_int_t id, json_t *data) { - NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - nm_auto_decref_json json_t *msg = NULL; - char *reply; - gboolean output_was_empty; - - output_was_empty = priv->output->len == 0; + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + nm_auto_decref_json json_t *msg = NULL; + nm_auto_free char *reply = NULL; msg = json_pack("{s:I, s:O}", "id", id, "result", data); reply = json_dumps(msg, 0); - g_string_append(priv->output, reply); - free(reply); - if (output_was_empty) - ovsdb_write(self); + _LOGT("send: echo: %s", reply); + + nm_str_buf_append(&priv->output_buf, reply); + + ovsdb_write_try(self); } /** @@ -2274,13 +2277,13 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg) /* This is a response to a method call. */ if (c_list_is_empty(&priv->calls_lst_head)) { - _LOGE("there are no queued calls expecting response %" G_GUINT64_FORMAT, (guint64) id); + _LOGW("there are no queued calls expecting response %" G_GUINT64_FORMAT, (guint64) id); ovsdb_disconnect(self, FALSE, FALSE); return; } call = c_list_first_entry(&priv->calls_lst_head, OvsdbMethodCall, calls_lst); if (call->call_id != id) { - _LOGE("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT, + _LOGW("expected a response to call %" G_GUINT64_FORMAT ", not %" G_GUINT64_FORMAT, call->call_id, (guint64) id); ovsdb_disconnect(self, FALSE, FALSE); @@ -2305,7 +2308,7 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg) /* Don't progress further commands in case the callback hit an error * and disconnected us. */ - if (!priv->conn) + if (priv->conn_fd < 0) return; /* Now we're free to serialize and send the next command, if any. */ @@ -2320,138 +2323,197 @@ ovsdb_got_msg(NMOvsdb *self, json_t *msg) /*****************************************************************************/ +typedef struct { + gsize bufp; + NMStrBuf *input; +} JsonReadMsgData; + /* Lower level marshalling and demarshalling of the JSON-RPC traffic on the * ovsdb socket. */ static size_t -_json_callback(void *buffer, size_t buflen, void *user_data) +_json_read_msg_cb(void *buffer, size_t buflen, void *user_data) { - NMOvsdb *self = NM_OVSDB(user_data); - NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + JsonReadMsgData *data = user_data; + + nm_assert(buffer); + nm_assert(buflen > 0); - if (priv->bufp == priv->input->len) { + if (data->bufp == data->input->len) { /* No more bytes buffered for decoding. */ return 0; } /* Pass one more byte to the JSON decoder. */ - *(char *) buffer = priv->input->str[priv->bufp]; - priv->bufp++; - - return (size_t) 1; + *(char *) buffer = nm_str_buf_get_char(data->input, data->bufp); + data->bufp++; + return 1; } -/** - * ovsdb_read_cb: - * - * Read out the data available from the ovsdb socket and try to deserialize - * the JSON. If we see a complete object, pass it upwards to ovsdb_got_msg(). - */ -static void -ovsdb_read_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +static json_t * +_json_read_msg(NMOvsdb *self, NMStrBuf *input) { - NMOvsdb *self = NM_OVSDB(user_data); - NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - GInputStream *stream = G_INPUT_STREAM(source_object); - GError *error = NULL; - gssize size; - json_t *msg; - json_error_t json_error = { + gs_free char *ss = NULL; + JsonReadMsgData data = { + .bufp = 0, + .input = input, + }; + json_error_t json_error = { 0, }; + json_t *msg; - size = g_input_stream_read_finish(stream, res, &error); - if (size == -1) { - /* ovsdb-server was possibly restarted */ - _LOGW("short read from ovsdb: %s", error->message); - priv->num_failures++; - g_clear_error(&error); - ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE); - return; - } + /* The callback always eats up only up to a single byte. This makes it + * possible for us to identify complete JSON objects in spite of us not + * knowing the length in advance. */ + msg = json_load_callback(_json_read_msg_cb, &data, JSON_DISABLE_EOF_CHECK, &json_error); + if (!msg) + return NULL; - g_string_append_len(priv->input, priv->buf, size); - do { - priv->bufp = 0; - /* The callback always eats up only up to a single byte. This makes - * it possible for us to identify complete JSON objects in spite of - * us not knowing the length in advance. */ - msg = json_load_callback(_json_callback, self, JSON_DISABLE_EOF_CHECK, &json_error); - if (msg) { - ovsdb_got_msg(self, msg); - g_string_erase(priv->input, 0, priv->bufp); - } - json_decref(msg); - } while (msg); + nm_assert(data.bufp > 0); - if (!priv->conn) - return; + _LOGT("json: parse %zu bytes: \"%s\"", + data.bufp, + (ss = g_strndup(nm_str_buf_get_str_at_unsafe(input, 0), data.bufp))); - if (size) - ovsdb_read(self); + nm_str_buf_erase(input, 0, data.bufp, FALSE); + return msg; } -static void -ovsdb_read(NMOvsdb *self) +static gboolean +_ovsdb_read_input_timeout_cb(gpointer user_data) { + NMOvsdb *self = user_data; NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - g_input_stream_read_async(g_io_stream_get_input_stream(G_IO_STREAM(priv->conn)), - priv->buf, - sizeof(priv->buf), - G_PRIORITY_DEFAULT, - NULL, - ovsdb_read_cb, - self); + _LOGW("invalid/incomplete data in receive buffer. Reset"); + priv->num_failures++; + ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE); + return G_SOURCE_CONTINUE; } static void -ovsdb_write_cb(GObject *source_object, GAsyncResult *res, gpointer user_data) +ovsdb_read(NMOvsdb *self) { - GOutputStream *stream = G_OUTPUT_STREAM(source_object); - NMOvsdb *self = NM_OVSDB(user_data); - NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - GError *error = NULL; + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); gssize size; - size = g_output_stream_write_finish(stream, res, &error); - if (size == -1) { +again: + size = nm_utils_fd_read(priv->conn_fd, &priv->input_buf); + + if (size <= 0) { + if (size == -EAGAIN) { + if (priv->input_buf.len == 0) + nm_clear_g_source_inst(&priv->input_timeout_source); + else if (!priv->input_timeout_source) { + /* We have data in the buffer but nothing further to read. Schedule a timer, + * if we don't get the rest within timeout, it means that the buffer + * content is broken (_json_read_msg() cannot extract any data) and + * we disconnect. */ + priv->input_timeout_source = + nm_g_timeout_add_seconds_source(5, _ovsdb_read_input_timeout_cb, NULL); + } + return; + } + /* ovsdb-server was possibly restarted */ - _LOGW("short write to ovsdb: %s", error->message); + _LOGW("short read from ovsdb: %s", nm_strerror_native(-size)); priv->num_failures++; - g_clear_error(&error); ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE); return; } - if (!priv->conn) - return; + nm_assert(priv->input_buf.len > 0); + + while (TRUE) { + nm_auto_decref_json json_t *msg = NULL; + + msg = _json_read_msg(self, &priv->input_buf); + if (!msg) + break; - g_string_erase(priv->output, 0, size); + nm_clear_g_source_inst(&priv->input_timeout_source); + ovsdb_got_msg(self, msg); - ovsdb_write(self); + if (priv->input_buf.len == 0) + break; + } + + if (priv->input_buf.len > 0) { + if (priv->input_buf.len > 50 * 1024 * 1024) { + _LOGW("received too much data from ovsdb that is not valid JSON"); + priv->num_failures++; + ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE); + return; + } + /* We have an incomplete message in the message buffer. Don't wait for another round + * of "poll", instead try to read it again. */ + goto again; + } + + nm_clear_g_source_inst(&priv->input_timeout_source); +} + +static gboolean +ovsdb_read_cb(int fd, GIOCondition condition, gpointer user_data) +{ + ovsdb_read(user_data); + return G_SOURCE_CONTINUE; } static void ovsdb_write(NMOvsdb *self) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - GOutputStream *stream; + gssize n; - if (!priv->output->len) +again: + if (priv->output_buf.len == 0) { + nm_clear_g_source_inst(&priv->conn_fd_out_source); return; + } + + n = write(priv->conn_fd, + nm_str_buf_get_str_at_unsafe(&priv->output_buf, 0), + priv->output_buf.len); + + if (n < 0) + n = -NM_ERRNO_NATIVE(errno); - stream = g_io_stream_get_output_stream(G_IO_STREAM(priv->conn)); - if (g_output_stream_has_pending(stream)) + if (n == -EAGAIN) { + if (!priv->conn_fd_out_source) { + priv->conn_fd_out_source = + nm_g_unix_fd_add_source(priv->conn_fd, G_IO_OUT, ovsdb_write_cb, self); + } return; + } + + if (n <= 0) { + /* ovsdb-server was possibly restarted */ + _LOGW("short write to ovsdb: %s", nm_strerror_native(-n)); + priv->num_failures++; + ovsdb_disconnect(self, priv->num_failures <= OVSDB_MAX_FAILURES, FALSE); + return; + } - g_output_stream_write_async(stream, - priv->output->str, - priv->output->len, - G_PRIORITY_DEFAULT, - NULL, - ovsdb_write_cb, - self); + nm_str_buf_erase(&priv->output_buf, 0, n, FALSE); + goto again; +} + +static void +ovsdb_write_try(NMOvsdb *self) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + + if (priv->conn_fd >= 0 && !priv->conn_fd_out_source) + ovsdb_write(self); +} + +static gboolean +ovsdb_write_cb(int fd, GIOCondition condition, gpointer user_data) +{ + ovsdb_write(user_data); + return G_SOURCE_CONTINUE; } /*****************************************************************************/ @@ -2474,7 +2536,7 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing) nm_assert(!retry || !is_disposing); - if (!priv->conn && !priv->conn_cancellable) + if (priv->conn_fd < 0 && !priv->conn_cancellable) return; _LOGD("disconnecting from ovsdb, retry %d", retry); @@ -2498,10 +2560,12 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing) _call_complete(call, NULL, error); } - priv->bufp = 0; - g_string_truncate(priv->input, 0); - g_string_truncate(priv->output, 0); - g_clear_object(&priv->conn); + nm_str_buf_reset(&priv->input_buf); + nm_str_buf_reset(&priv->output_buf); + nm_clear_fd(&priv->conn_fd); + nm_clear_g_source_inst(&priv->conn_fd_in_source); + nm_clear_g_source_inst(&priv->conn_fd_out_source); + nm_clear_g_source_inst(&priv->input_timeout_source); nm_clear_g_free(&priv->db_uuid); nm_clear_g_cancellable(&priv->conn_cancellable); @@ -2702,15 +2766,12 @@ _ovsdb_connect_complete_with_fd(NMOvsdb *self, int fd_take) gs_unref_object GSocket *socket = NULL; gs_free_error GError *error = NULL; - socket = g_socket_new_from_fd(nm_steal_fd(&fd_take), &error); - if (!socket) { - _LOGT("connect: failure to open socket for new FD: %s", error->message); - ovsdb_disconnect(self, FALSE, FALSE); - return; - } + nm_clear_g_cancellable(&priv->conn_cancellable); + + nm_io_fcntl_setfl_update_nonblock(fd_take); - priv->conn = g_socket_connection_factory_create_connection(socket); - g_clear_object(&priv->conn_cancellable); + priv->conn_fd = nm_steal_fd(&fd_take); + priv->conn_fd_in_source = nm_g_unix_fd_add_source(priv->conn_fd, G_IO_IN, ovsdb_read_cb, self); ovsdb_read(self); ovsdb_next_command(self); @@ -2784,7 +2845,7 @@ ovsdb_try_connect(NMOvsdb *self) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); - if (priv->conn || priv->conn_cancellable) + if (priv->conn_fd >= 0 || priv->conn_cancellable) return; _LOGT("connect: start connecting socket %s on idle", NM_OVSDB_SOCKET); @@ -2964,11 +3025,15 @@ nm_ovsdb_init(NMOvsdb *self) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + priv->conn_fd = -1; + + priv->input_buf = NM_STR_BUF_INIT(0, FALSE); + priv->output_buf = NM_STR_BUF_INIT(0, FALSE); + c_list_init(&priv->calls_lst_head); priv->platform = g_object_ref(NM_PLATFORM_GET); - priv->input = g_string_new(NULL); - priv->output = g_string_new(NULL); + priv->bridges = g_hash_table_new_full(nm_pstr_hash, nm_pstr_equal, (GDestroyNotify) _free_bridge, NULL); priv->ports = @@ -2989,14 +3054,8 @@ dispose(GObject *object) nm_assert(c_list_is_empty(&priv->calls_lst_head)); - if (priv->input) { - g_string_free(priv->input, TRUE); - priv->input = NULL; - } - if (priv->output) { - g_string_free(priv->output, TRUE); - priv->output = NULL; - } + nm_str_buf_destroy(&priv->input_buf); + nm_str_buf_destroy(&priv->output_buf); g_clear_object(&priv->platform); nm_clear_pointer(&priv->bridges, g_hash_table_destroy); diff --git a/src/core/devices/team/nm-device-team.c b/src/core/devices/team/nm-device-team.c index 5c955986..4d748362 100644 --- a/src/core/devices/team/nm-device-team.c +++ b/src/core/devices/team/nm-device-team.c @@ -899,8 +899,13 @@ attach_port(NMDevice *device, return TRUE; } -static void -detach_port(NMDevice *device, NMDevice *port, gboolean configure) +static NMTernary +detach_port(NMDevice *device, + NMDevice *port, + gboolean configure, + GCancellable *cancellable, + NMDeviceAttachPortCallback callback, + gpointer user_data) { NMDeviceTeam *self = NM_DEVICE_TEAM(device); NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE(self); @@ -950,6 +955,8 @@ detach_port(NMDevice *device, NMDevice *port, gboolean configure) _update_port_config(self, port_iface, "{}"); g_hash_table_remove(priv->port_configs, port_iface); } + + return TRUE; } static gboolean diff --git a/src/core/devices/wifi/nm-device-iwd-p2p.c b/src/core/devices/wifi/nm-device-iwd-p2p.c index 40e38321..73fd4716 100644 --- a/src/core/devices/wifi/nm-device-iwd-p2p.c +++ b/src/core/devices/wifi/nm-device-iwd-p2p.c @@ -126,14 +126,17 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMSettingWifiP2P *s_wifi_p2p; GBytes *wfd_ies; NMSettingIPConfig *s_ip; if (!NM_DEVICE_CLASS(nm_device_iwd_p2p_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_wifi_p2p = diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index e03227cd..47407a1e 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -159,7 +159,7 @@ ap_add_remove(NMDeviceIwd *self, } if (priv->enabled && !priv->iwd_autoconnect) - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); if (recheck_available_connections) nm_device_recheck_available_connections(NM_DEVICE(self)); @@ -208,7 +208,7 @@ remove_all_aps(NMDeviceIwd *self) ap_add_remove(self, FALSE, ap, FALSE); if (!priv->iwd_autoconnect) - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); nm_device_recheck_available_connections(NM_DEVICE(self)); } @@ -401,7 +401,7 @@ get_ordered_networks_cb(GObject *source, GAsyncResult *res, gpointer user_data) if (changed) { if (!priv->iwd_autoconnect) - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); nm_device_recheck_available_connections(NM_DEVICE(self)); } @@ -723,7 +723,10 @@ is_ap_known_network(NMIwdManager *manager, NMWifiAP *ap) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceIwd *self = NM_DEVICE_IWD(device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); @@ -739,7 +742,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * gsize ssid_len; if (!NM_DEVICE_CLASS(nm_device_iwd_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_wireless = nm_connection_get_setting_wireless(connection); @@ -1682,7 +1685,7 @@ failed: if (!priv->nm_autoconnect) { priv->nm_autoconnect = true; - nm_device_emit_recheck_auto_activate(device); + nm_device_recheck_auto_activate_schedule(device); } } g_variant_unref(value); @@ -2302,9 +2305,10 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason) * to reset the retry count so we set no timeout. */ if (priv->iwd_autoconnect) { - NMSettingsConnection *sett_conn = nm_act_request_get_settings_connection(req); - - nm_settings_connection_autoconnect_retries_set(sett_conn, 0); + nm_manager_devcon_autoconnect_retries_set(nm_device_get_manager(device), + device, + nm_act_request_get_settings_connection(req), + 0); } /* With priv->iwd_autoconnect, if we're assuming a connection because @@ -2908,7 +2912,7 @@ state_changed(NMDeviceIwd *self, const char *new_state) if (!priv->iwd_autoconnect && NM_IN_STRSET(new_state, "disconnected")) { priv->nm_autoconnect = TRUE; if (!can_connect) - nm_device_emit_recheck_auto_activate(device); + nm_device_recheck_auto_activate_schedule(device); } } @@ -3104,12 +3108,12 @@ config_changed(NMConfig *config, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); gboolean old_iwd_ac = priv->iwd_autoconnect; - priv->iwd_autoconnect = - nm_config_data_get_device_config_boolean(config_data, - NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_IWD_AUTOCONNECT, - NM_DEVICE(self), - TRUE, - TRUE); + priv->iwd_autoconnect = nm_config_data_get_device_config_boolean_by_device( + config_data, + NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_IWD_AUTOCONNECT, + NM_DEVICE(self), + TRUE, + TRUE); if (old_iwd_ac != priv->iwd_autoconnect && priv->dbus_station_proxy && !priv->current_ap) { gs_unref_variant GVariant *value = NULL; diff --git a/src/core/devices/wifi/nm-device-olpc-mesh.c b/src/core/devices/wifi/nm-device-olpc-mesh.c index 4705f75c..436c7847 100644 --- a/src/core/devices/wifi/nm-device-olpc-mesh.c +++ b/src/core/devices/wifi/nm-device-olpc-mesh.c @@ -270,7 +270,7 @@ companion_state_changed_cb(NMDeviceWifi *companion, NMDeviceState self_state = nm_device_get_state(NM_DEVICE(self)); if (old_state > NM_DEVICE_STATE_DISCONNECTED && state <= NM_DEVICE_STATE_DISCONNECTED) { - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); } if (self_state < NM_DEVICE_STATE_PREPARE || self_state > NM_DEVICE_STATE_ACTIVATED diff --git a/src/core/devices/wifi/nm-device-wifi-p2p.c b/src/core/devices/wifi/nm-device-wifi-p2p.c index 424464c1..fa8cb8fa 100644 --- a/src/core/devices/wifi/nm-device-wifi-p2p.c +++ b/src/core/devices/wifi/nm-device-wifi-p2p.c @@ -233,10 +233,13 @@ is_available(NMDevice *device, NMDeviceCheckDevAvailableFlags flags) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { if (!NM_DEVICE_CLASS(nm_device_wifi_p2p_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; /* TODO: Allow limitting the interface using the HW-address? */ diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c index 03625f8d..43772834 100644 --- a/src/core/devices/wifi/nm-device-wifi.c +++ b/src/core/devices/wifi/nm-device-wifi.c @@ -483,7 +483,7 @@ _scan_notify_is_scanning(NMDeviceWifi *self) if (!_scan_is_scanning_eval(priv)) { if (state <= NM_DEVICE_STATE_DISCONNECTED || state > NM_DEVICE_STATE_ACTIVATED) - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); nm_device_remove_pending_action(NM_DEVICE(self), NM_PENDING_ACTION_WIFI_SCAN, FALSE); } @@ -843,7 +843,7 @@ ap_add_remove(NMDeviceWifi *self, nm_dbus_object_clear_and_unexport(&ap); } - nm_device_emit_recheck_auto_activate(NM_DEVICE(self)); + nm_device_recheck_auto_activate_schedule(NM_DEVICE(self)); if (recheck_available_connections) nm_device_recheck_available_connections(NM_DEVICE(self)); } @@ -981,7 +981,10 @@ deactivate_reset_hw_addr(NMDevice *device) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { NMDeviceWifi *self = NM_DEVICE_WIFI(device); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE(self); @@ -995,7 +998,7 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError * const char *key_mgmt; if (!NM_DEVICE_CLASS(nm_device_wifi_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; s_wireless = nm_connection_get_setting_wireless(connection); @@ -1395,7 +1398,7 @@ _hw_addr_set_scanning(NMDeviceWifi *self, gboolean do_reset) priv = NM_DEVICE_WIFI_GET_PRIVATE(self); - randomize = nm_config_data_get_device_config_boolean( + randomize = nm_config_data_get_device_config_boolean_by_device( NM_CONFIG_GET_DATA, NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS, device, @@ -1428,7 +1431,7 @@ _hw_addr_set_scanning(NMDeviceWifi *self, gboolean do_reset) * a new one.*/ priv->hw_addr_scan_expire = now + SCAN_RAND_MAC_ADDRESS_EXPIRE_SEC; - generate_mac_address_mask = nm_config_data_get_device_config( + generate_mac_address_mask = nm_config_data_get_device_config_by_device( NM_CONFIG_GET_DATA, NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK, device, diff --git a/src/core/devices/wwan/nm-device-modem.c b/src/core/devices/wwan/nm-device-modem.c index b83120f9..a1050c3f 100644 --- a/src/core/devices/wwan/nm-device-modem.c +++ b/src/core/devices/wwan/nm-device-modem.c @@ -383,12 +383,15 @@ get_type_description(NMDevice *device) } static gboolean -check_connection_compatible(NMDevice *device, NMConnection *connection, GError **error) +check_connection_compatible(NMDevice *device, + NMConnection *connection, + gboolean check_properties, + GError **error) { GError *local = NULL; if (!NM_DEVICE_CLASS(nm_device_modem_parent_class) - ->check_connection_compatible(device, connection, error)) + ->check_connection_compatible(device, connection, check_properties, error)) return FALSE; if (!nm_modem_check_connection_compatible(NM_DEVICE_MODEM_GET_PRIVATE(device)->modem, diff --git a/src/core/devices/wwan/nm-modem-broadband.c b/src/core/devices/wwan/nm-modem-broadband.c index f0907c46..a150040f 100644 --- a/src/core/devices/wwan/nm-modem-broadband.c +++ b/src/core/devices/wwan/nm-modem-broadband.c @@ -20,6 +20,8 @@ #define NM_MODEM_BROADBAND_MODEM "modem" +#define MM_SUPPORTS_INITIAL_EPS_BEARER_SETTINGS MM_CHECK_VERSION(1, 10, 0) + #if !MM_CHECK_VERSION(1, 14, 0) #define MM_MODEM_CAPABILITY_5GNR ((MMModemCapability) (1 << 6)) #endif @@ -44,6 +46,7 @@ typedef enum { CONNECT_STEP_WAIT_FOR_SIM, CONNECT_STEP_UNLOCK, CONNECT_STEP_WAIT_FOR_READY, + CONNECT_STEP_INTIAL_EPS_BEARER, CONNECT_STEP_CONNECT, CONNECT_STEP_LAST, } ConnectStep; @@ -560,6 +563,36 @@ out: return TRUE; } +#if MM_SUPPORTS_INITIAL_EPS_BEARER_SETTINGS +static void +set_initial_eps_bearer_settings_ready(MMModem3gpp *modem_3gpp_iface, + GAsyncResult *res, + NMModemBroadband *self) +{ + gs_free_error GError *error = NULL; + + if (!mm_modem_3gpp_set_initial_eps_bearer_settings_finish(modem_3gpp_iface, res, &error)) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + if (!g_error_matches(error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) { + _LOGW("failed to set initial EPS bearer settings: %s", error->message); + nm_modem_emit_prepare_result(NM_MODEM(self), + FALSE, + NM_DEVICE_STATE_REASON_GSM_APN_FAILED); + connect_context_clear(self); + return; + } + + _LOGD("failed to set initial EPS bearer settings due to lack of support: %s", + error->message); + } + + self->_priv.ctx->step++; + connect_context_step(self); +} +#endif + static void connect_context_step(NMModemBroadband *self) { @@ -629,6 +662,56 @@ connect_context_step(NMModemBroadband *self) ctx->step++; } /* fall-through */ + + case CONNECT_STEP_INTIAL_EPS_BEARER: + if (MODEM_CAPS_3GPP(ctx->caps)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm(ctx->connection); + const char *apn = nm_setting_gsm_get_initial_eps_apn(s_gsm); + gboolean do_config = nm_setting_gsm_get_initial_eps_config(s_gsm); + + /* assume do_config is true if an APN is set */ + if (apn || do_config) { +#if MM_SUPPORTS_INITIAL_EPS_BEARER_SETTINGS + gs_unref_object MMBearerProperties *config = NULL; + NMModemIPType ip_type = nm_modem_get_initial_eps_bearer_ip_type(ctx->ip_types); + + config = mm_bearer_properties_new(); + switch (ip_type) { + case NM_MODEM_IP_TYPE_IPV4: + mm_bearer_properties_set_ip_type(config, MM_BEARER_IP_FAMILY_IPV4); + break; + case NM_MODEM_IP_TYPE_IPV6: + mm_bearer_properties_set_ip_type(config, MM_BEARER_IP_FAMILY_IPV6); + break; + case NM_MODEM_IP_TYPE_IPV4V6: + mm_bearer_properties_set_ip_type(config, MM_BEARER_IP_FAMILY_IPV4V6); + break; + default: + /* do nothing */ + break; + } + if (apn) + mm_bearer_properties_set_apn(config, apn); + + /* + * Setting the initial EPS bearer settings is a no-op in + * ModemManager if the desired configuration is already active. + */ + mm_modem_3gpp_set_initial_eps_bearer_settings( + self->_priv.modem_3gpp_iface, + config, + ctx->cancellable, + (GAsyncReadyCallback) set_initial_eps_bearer_settings_ready, + self); + break; +#else + _LOGD("cannot set initial EPS bearer settings due to old ModemManager version"); +#endif + } + } + ctx->step++; + /* fall-through */ + case CONNECT_STEP_CONNECT: if (!ctx->connect_properties) break; diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c index 0159d351..ea0fa7aa 100644 --- a/src/core/devices/wwan/nm-modem.c +++ b/src/core/devices/wwan/nm-modem.c @@ -558,6 +558,37 @@ nm_modem_get_connection_ip_type(NMModem *self, NMConnection *connection, GError return NULL; } +/** + * nm_modem_get_initial_eps_bearer_ip_type: + * @connection_ip_types: the #NMModemIPType as returned by + * nm_modem_get_connection_ip_type + * + * Given the connection IP types, this function returns which IP type to use when + * configuring the initial EPS bearer. + * + * Returns: the #NMModemIpType value to use for the initial EPS bearer + */ +NMModemIPType +nm_modem_get_initial_eps_bearer_ip_type(const GArray *connection_ip_types) +{ + NMModemIPType ip_types = NM_MODEM_IP_TYPE_UNKNOWN; + guint i; + + nm_assert(connection_ip_types); + + for (i = 0; i < connection_ip_types->len; i++) + ip_types |= nm_g_array_index(connection_ip_types, NMModemIPType, i); + + nm_assert(ip_types != NM_MODEM_IP_TYPE_UNKNOWN); + + if (ip_types & NM_MODEM_IP_TYPE_IPV4V6) + return NM_MODEM_IP_TYPE_IPV4V6; + if (ip_types & NM_MODEM_IP_TYPE_IPV4) + return NM_MODEM_IP_TYPE_IPV4; + + return NM_MODEM_IP_TYPE_IPV6; +} + const char * nm_modem_get_device_id(NMModem *self) { diff --git a/src/core/devices/wwan/nm-modem.h b/src/core/devices/wwan/nm-modem.h index ec001102..021d77b2 100644 --- a/src/core/devices/wwan/nm-modem.h +++ b/src/core/devices/wwan/nm-modem.h @@ -226,6 +226,8 @@ void nm_modem_emit_ppp_failed(NMModem *self, NMDeviceStateReason reason); GArray *nm_modem_get_connection_ip_type(NMModem *self, NMConnection *connection, GError **error); +NMModemIPType nm_modem_get_initial_eps_bearer_ip_type(const GArray *connection_ip_types); + /* For subclasses */ void nm_modem_emit_signal_new_config(NMModem *self, |