diff options
Diffstat (limited to 'src/core/devices')
| -rw-r--r-- | src/core/devices/nm-device-bond.c | 95 | ||||
| -rw-r--r-- | src/core/devices/nm-device-private.h | 5 | ||||
| -rw-r--r-- | src/core/devices/nm-device-veth.c | 4 | ||||
| -rw-r--r-- | src/core/devices/nm-device.c | 96 | ||||
| -rw-r--r-- | src/core/devices/nm-device.h | 5 | ||||
| -rw-r--r-- | src/core/devices/ovs/nm-ovsdb.c | 155 |
6 files changed, 271 insertions, 89 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c index 44bb316d..3e083de4 100644 --- a/src/core/devices/nm-device-bond.c +++ b/src/core/devices/nm-device-bond.c @@ -395,61 +395,57 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p { const char *opt_value; +#define _v_fcn(fcn, s_bond, opt) (fcn(nm_setting_bond_get_option_normalized((s_bond), (opt)))) +#define _v_u8(s_bond, opt) _nm_setting_bond_opt_value_as_u8((s_bond), (opt)) +#define _v_u16(s_bond, opt) _nm_setting_bond_opt_value_as_u16((s_bond), (opt)) +#define _v_u32(s_bond, opt) _nm_setting_bond_opt_value_as_u32((s_bond), (opt)) +#define _v_intbool(s_bond, opt) _nm_setting_bond_opt_value_as_intbool((s_bond), (opt)) + *props = (NMPlatformLnkBond){ - .mode = _nm_setting_bond_mode_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_MODE)), + .mode = _v_fcn(_nm_setting_bond_mode_from_string, s_bond, NM_SETTING_BOND_OPTION_MODE), .primary = _setting_bond_primary_opt_as_ifindex(s_bond), - .miimon = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_MIIMON), - .updelay = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_UPDELAY), - .downdelay = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY), - .arp_interval = - _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL), - .resend_igmp = - _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP), - .min_links = _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS), - .lp_interval = - _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL), - .packets_per_port = - _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE), - .peer_notif_delay = - _nm_setting_bond_opt_value_as_u32(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY), - .arp_all_targets = _nm_setting_bond_arp_all_targets_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS)), - .arp_validate = _nm_setting_bond_arp_validate_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE)), - .ad_actor_sys_prio = - _nm_setting_bond_opt_value_as_u16(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO), - .ad_user_port_key = - _nm_setting_bond_opt_value_as_u16(s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY), - .primary_reselect = _nm_setting_bond_primary_reselect_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT)), - .fail_over_mac = _nm_setting_bond_fail_over_mac_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC)), - .xmit_hash_policy = _nm_setting_bond_xmit_hash_policy_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY)), - .num_grat_arp = - _nm_setting_bond_opt_value_as_u8(s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP), - .all_ports_active = - _nm_setting_bond_opt_value_as_u8(s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE), - .lacp_rate = _nm_setting_bond_lacp_rate_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_LACP_RATE)), - .ad_select = _nm_setting_bond_ad_select_from_string( - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_SELECT)), + .miimon = _v_u32(s_bond, NM_SETTING_BOND_OPTION_MIIMON), + .updelay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_UPDELAY), + .downdelay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY), + .arp_interval = _v_u32(s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL), + .resend_igmp = _v_u32(s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP), + .min_links = _v_u32(s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS), + .lp_interval = _v_u32(s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL), + .packets_per_port = _v_u32(s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE), + .peer_notif_delay = _v_u32(s_bond, NM_SETTING_BOND_OPTION_PEER_NOTIF_DELAY), + .arp_all_targets = _v_fcn(_nm_setting_bond_arp_all_targets_from_string, + s_bond, + NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS), + .arp_validate = _v_fcn(_nm_setting_bond_arp_validate_from_string, + s_bond, + NM_SETTING_BOND_OPTION_ARP_VALIDATE), + .ad_actor_sys_prio = _v_u16(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO), + .ad_user_port_key = _v_u16(s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY), + .primary_reselect = _v_fcn(_nm_setting_bond_primary_reselect_from_string, + s_bond, + NM_SETTING_BOND_OPTION_PRIMARY_RESELECT), + .fail_over_mac = _v_fcn(_nm_setting_bond_fail_over_mac_from_string, + s_bond, + NM_SETTING_BOND_OPTION_FAIL_OVER_MAC), + .xmit_hash_policy = _v_fcn(_nm_setting_bond_xmit_hash_policy_from_string, + s_bond, + 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), + .lacp_rate = _v_fcn(_nm_setting_bond_lacp_rate_from_string, + s_bond, + NM_SETTING_BOND_OPTION_LACP_RATE), + .ad_select = _v_fcn(_nm_setting_bond_ad_select_from_string, + s_bond, + NM_SETTING_BOND_OPTION_AD_SELECT), + .use_carrier = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER), + .tlb_dynamic_lb = _v_intbool(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB), }; nm_ether_addr_from_string( &props->ad_actor_system, nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)); - opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER); - if (opt_value != NULL) - props->use_carrier = _nm_utils_ascii_str_to_bool(opt_value, FALSE); - - opt_value = - nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB); - if (opt_value != NULL) - props->tlb_dynamic_lb = _nm_utils_ascii_str_to_bool(opt_value, FALSE); - opt_value = nm_setting_bond_get_option_normalized(s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); if (opt_value != NULL) props->arp_ip_targets_num = @@ -459,7 +455,6 @@ _platform_lnk_bond_init_from_setting(NMSettingBond *s_bond, NMPlatformLnkBond *p props->updelay_has = props->miimon_has && props->miimon; props->downdelay_has = props->miimon_has && props->miimon; props->peer_notif_delay_has = (props->miimon || props->arp_interval) && props->peer_notif_delay; - props->arp_all_targets_has = props->arp_interval && props->arp_all_targets; 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); @@ -493,6 +488,10 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason) if (!nm_device_hw_addr_set_cloned(device, nm_device_get_applied_connection(device), FALSE)) ret = NM_ACT_STAGE_RETURN_FAILURE; } + + /* This is a workaround because netlink do not support ifname as primary */ + set_bond_attr_or_default(device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + nm_device_bring_up(device, TRUE, NULL); return ret; diff --git a/src/core/devices/nm-device-private.h b/src/core/devices/nm-device-private.h index b54aed6a..31424d5c 100644 --- a/src/core/devices/nm-device-private.h +++ b/src/core/devices/nm-device-private.h @@ -89,7 +89,10 @@ nm_device_devip_set_state(NMDevice *self, nm_assert(NM_IS_DEVICE(self)); nm_assert_addr_family_or_unspec(addr_family); nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd)); - nm_assert(NM_IN_SET(ip_state, NM_DEVICE_IP_STATE_PENDING, NM_DEVICE_IP_STATE_READY)); + nm_assert(NM_IN_SET(ip_state, + NM_DEVICE_IP_STATE_NONE, + NM_DEVICE_IP_STATE_PENDING, + NM_DEVICE_IP_STATE_READY)); nm_device_devip_set_state_full(self, addr_family, ip_state, l3cd, NM_DEVICE_STATE_REASON_NONE); } diff --git a/src/core/devices/nm-device-veth.c b/src/core/devices/nm-device-veth.c index 63dfd8bb..c3482e78 100644 --- a/src/core/devices/nm-device-veth.c +++ b/src/core/devices/nm-device-veth.c @@ -101,8 +101,8 @@ create_and_realize(NMDevice *device, peer = nm_setting_veth_get_peer(s_veth); peer_device = nm_manager_get_device(NM_MANAGER_GET, peer, NM_DEVICE_TYPE_VETH); if (peer_device) { - /* The veth device and its peer already exist. No need to create it again. */ - if (nm_streq0(nm_device_get_iface(nm_device_parent_get_device(peer_device)), iface)) + if (nm_device_parent_get_device(peer_device)) + /* The veth device and its peer already exist. No need to create it again. */ return TRUE; } diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index d63b902b..6be9bc7c 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -96,6 +96,8 @@ #define NM_DEVICE_AUTH_RETRIES_INFINITY -2 #define NM_DEVICE_AUTH_RETRIES_DEFAULT 3 +#define AUTOCONNECT_RESET_RETRIES_TIMER 300 + /*****************************************************************************/ typedef void (*ActivationHandleFunc)(NMDevice *self); @@ -761,6 +763,9 @@ typedef struct _NMDevicePrivate { GVariant *ports_variant; /* Array of port devices D-Bus path */ char *prop_ip_iface; /* IP interface D-Bus property */ + + int autoconnect_retries; + gint32 autoconnect_retries_blocked_until; } NMDevicePrivate; G_DEFINE_ABSTRACT_TYPE(NMDevice, nm_device, NM_TYPE_DBUS_OBJECT) @@ -778,7 +783,9 @@ static void _dev_l3_cfg_commit_type_reset(NMDevice *self); static gboolean nm_device_master_add_slave(NMDevice *self, NMDevice *slave, gboolean configure); static void nm_device_slave_notify_enslave(NMDevice *self, gboolean success); -static void nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason); +static void nm_device_slave_notify_release(NMDevice *self, + NMDeviceStateReason reason, + ReleaseSlaveType release_type); static void _dev_ipll6_start(NMDevice *self); @@ -6236,7 +6243,7 @@ nm_device_master_release_slave(NMDevice *self, release_type >= RELEASE_SLAVE_TYPE_CONFIG); /* raise notifications about the release, including clearing is_enslaved. */ - nm_device_slave_notify_release(slave, reason); + nm_device_slave_notify_release(slave, reason, release_type); /* keep both alive until the end of the function. * Transfers ownership from slave_priv->master. */ @@ -6536,12 +6543,16 @@ device_recheck_slave_status(NMDevice *self, const NMPlatformLink *plink) g_return_if_fail(plink); - if (plink->master <= 0) - goto out; - - master = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, plink->master); - plink_master = nm_platform_link_get(nm_device_get_platform(self), plink->master); - plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master)); + if (plink->master > 0) { + master = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, plink->master); + plink_master = nm_platform_link_get(nm_device_get_platform(self), plink->master); + plink_master_keep_alive = nmp_object_ref(NMP_OBJECT_UP_CAST(plink_master)); + } else { + if (priv->master_ifindex == 0) + goto out; + master = NULL; + plink_master = NULL; + } if (master == NULL && plink_master && NM_IN_STRSET(plink_master->name, "ovs-system", "ovs-netdev") @@ -8024,7 +8035,9 @@ nm_device_slave_notify_enslave(NMDevice *self, gboolean success) * Notifies a slave that it has been released, and why. */ static void -nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason) +nm_device_slave_notify_release(NMDevice *self, + NMDeviceStateReason reason, + ReleaseSlaveType release_type) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); NMConnection *connection = nm_device_get_applied_connection(self); @@ -8032,7 +8045,7 @@ nm_device_slave_notify_release(NMDevice *self, NMDeviceStateReason reason) g_return_if_fail(priv->master); - if (!priv->is_enslaved) + if (!priv->is_enslaved && release_type == RELEASE_SLAVE_TYPE_NO_CONFIG) return; if (priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) { @@ -8956,7 +8969,7 @@ nm_device_emit_recheck_assume(gpointer user_data) priv = NM_DEVICE_GET_PRIVATE(self); priv->recheck_assume_id = 0; - if (!nm_device_get_act_request(self)) + if (!priv->queued_act_request && !nm_device_get_act_request(self)) g_signal_emit(self, signals[RECHECK_ASSUME], 0); return G_SOURCE_REMOVE; @@ -9954,6 +9967,7 @@ nm_device_devip_set_state_full(NMDevice *self, nm_assert_addr_family_or_unspec(addr_family); nm_assert(NM_IN_SET(ip_state, + NM_DEVICE_IP_STATE_NONE, NM_DEVICE_IP_STATE_PENDING, NM_DEVICE_IP_STATE_READY, NM_DEVICE_IP_STATE_FAILED)); @@ -9961,7 +9975,7 @@ nm_device_devip_set_state_full(NMDevice *self, nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) == (failed_reason == NM_DEVICE_STATE_REASON_NONE)); - nm_assert((ip_state != NM_DEVICE_IP_STATE_FAILED) || !l3cd); + nm_assert(NM_IN_SET(ip_state, NM_DEVICE_IP_STATE_PENDING, NM_DEVICE_IP_STATE_READY) || !l3cd); p = _dev_ipdev_data(self, addr_family); @@ -12206,7 +12220,7 @@ _dev_ipsharedx_cleanup(NMDevice *self, int addr_family) } if (priv->ipshared_data_4.v4.firewall_config) { - nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, FALSE); + nm_firewall_config_apply_sync(priv->ipshared_data_4.v4.firewall_config, FALSE); nm_clear_pointer(&priv->ipshared_data_4.v4.firewall_config, nm_firewall_config_free); } @@ -12353,8 +12367,8 @@ _dev_ipshared4_start(NMDevice *self) goto out_fail; priv->ipshared_data_4.v4.firewall_config = - nm_firewall_config_new(ip_iface, ip4_addr.address, ip4_addr.plen); - nm_firewall_config_apply(priv->ipshared_data_4.v4.firewall_config, TRUE); + nm_firewall_config_new_shared(ip_iface, ip4_addr.address, ip4_addr.plen); + nm_firewall_config_apply_sync(priv->ipshared_data_4.v4.firewall_config, TRUE); priv->ipshared_data_4.v4.l3cd = nm_l3_config_data_ref(l3cd); _dev_l3_register_l3cds_set_one(self, L3_CONFIG_DATA_TYPE_SHARED_4, l3cd, FALSE); @@ -16857,6 +16871,49 @@ nm_device_get_initial_hw_address(NMDevice *self) return NM_DEVICE_GET_PRIVATE(self)->hw_addr_initial; } +void +nm_device_set_autoconnect_retries(NMDevice *self, int tries) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + if (priv->autoconnect_retries != tries) { + _LOGT(LOGD_DEVICE, "autoconnect: retries set %d", tries); + priv->autoconnect_retries = tries; + } + + if (tries) + priv->autoconnect_retries_blocked_until = 0; /* we are not blocked anymore */ + else + priv->autoconnect_retries_blocked_until = + nm_utils_get_monotonic_timestamp_sec() + AUTOCONNECT_RESET_RETRIES_TIMER; +} + +int +nm_device_get_autoconnect_retries(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + return priv->autoconnect_retries; +} + +gint32 +nm_device_autoconnect_retries_blocked_until(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + return priv->autoconnect_retries_blocked_until; +} + +void +nm_device_autoconnect_retries_reset(NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + + /* default value, we will sync. with connection value when needed */ + priv->autoconnect_retries = -2; + priv->autoconnect_retries_blocked_until = 0; +} + /** * nm_device_spec_match_list: * @self: an #NMDevice @@ -17161,6 +17218,13 @@ nm_device_get_hostname_from_dns_lookup(NMDevice *self, int addr_family, gboolean /* If the device is not supposed to have addresses, * return an immediate empty result.*/ if (!nm_device_get_applied_connection(self)) { + nm_clear_pointer(&priv->hostname_resolver_x[IS_IPv4], _hostname_resolver_free); + NM_SET_OUT(out_wait, FALSE); + return NULL; + } + + if (!priv->carrier) { + nm_clear_pointer(&priv->hostname_resolver_x[IS_IPv4], _hostname_resolver_free); NM_SET_OUT(out_wait, FALSE); return NULL; } @@ -17601,6 +17665,8 @@ nm_device_init(NMDevice *self) priv->sys_iface_state_ = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL; priv->promisc_reset = NM_OPTION_BOOL_DEFAULT; + + priv->autoconnect_retries = -2; } static GObject * diff --git a/src/core/devices/nm-device.h b/src/core/devices/nm-device.h index de850e68..fea46bb7 100644 --- a/src/core/devices/nm-device.h +++ b/src/core/devices/nm-device.h @@ -464,6 +464,11 @@ const char *nm_device_get_permanent_hw_address_full(NMDevice *self, gboolean *out_is_fake); const char *nm_device_get_initial_hw_address(NMDevice *dev); +void nm_device_set_autoconnect_retries(NMDevice *self, int tries); +int nm_device_get_autoconnect_retries(NMDevice *self); +gint32 nm_device_autoconnect_retries_blocked_until(NMDevice *self); +void nm_device_autoconnect_retries_reset(NMDevice *self); + NMDhcpConfig *nm_device_get_dhcp_config(NMDevice *dev, int addr_family); NML3Cfg *nm_device_get_l3cfg(NMDevice *self); diff --git a/src/core/devices/ovs/nm-ovsdb.c b/src/core/devices/ovs/nm-ovsdb.c index e7c96852..d3e858a1 100644 --- a/src/core/devices/ovs/nm-ovsdb.c +++ b/src/core/devices/ovs/nm-ovsdb.c @@ -18,6 +18,7 @@ #include "nm-manager.h" #include "nm-setting-ovs-external-ids.h" #include "nm-priv-helper-call.h" +#include "libnm-platform/nm-platform.h" /*****************************************************************************/ @@ -120,6 +121,7 @@ enum { static guint signals[LAST_SIGNAL] = {0}; typedef struct { + NMPlatform *platform; GSocketConnection *conn; GCancellable *conn_cancellable; char buf[4096]; /* Input buffer */ @@ -135,8 +137,14 @@ typedef struct { GHashTable *bridges; /* bridge uuid => OpenvswitchBridge */ char *db_uuid; guint num_failures; - guint num_pending_deletions; bool ready : 1; + struct { + GPtrArray *interfaces; /* Interface names we are waiting to go away */ + GSource *timeout_source; /* After all deletions complete, wait this + * timeout for interfaces to disappear */ + gulong link_changed_id; /* Platform link-changed signal handle */ + guint num_pending_del; /* Number of ovsdb deletions pending */ + } cleanup; } NMOvsdbPrivate; struct _NMOvsdb { @@ -161,6 +169,7 @@ static void ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposin 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); /*****************************************************************************/ @@ -2283,21 +2292,114 @@ ovsdb_disconnect(NMOvsdb *self, gboolean retry, gboolean is_disposing) } static void -_check_ready(NMOvsdb *self) +cleanup_emit_ready(NMOvsdb *self, const char *reason) +{ + NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + + _LOGT("cleanup: ready (%s)", reason); + + nm_clear_pointer(&priv->cleanup.interfaces, g_ptr_array_unref); + nm_clear_g_source_inst(&priv->cleanup.timeout_source); + nm_clear_g_signal_handler(priv->platform, &priv->cleanup.link_changed_id); + + priv->ready = TRUE; + g_signal_emit(self, signals[READY], 0); + nm_manager_unblock_failed_ovs_interfaces(nm_manager_get()); +} + +static gboolean +cleanup_timeout(NMOvsdb *self) +{ + cleanup_emit_ready(self, "timeout"); + return G_SOURCE_CONTINUE; +} + +static void +cleanup_link_cb(NMPlatform *platform, + int obj_type_i, + int ifindex, + NMPlatformLink *plink, + int change_type_i, + gpointer user_data) +{ + const NMPlatformSignalChangeType change_type = change_type_i; + + if (change_type != NM_PLATFORM_SIGNAL_REMOVED) + return; + + cleanup_check_ready(user_data); +} + +static void +cleanup_check_ready(NMOvsdb *self) { NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE(self); + guint i = 0; nm_assert(!priv->ready); - if (priv->num_pending_deletions == 0) { - priv->ready = TRUE; - g_signal_emit(self, signals[READY], 0); - nm_manager_unblock_failed_ovs_interfaces(nm_manager_get()); + if (priv->cleanup.num_pending_del > 0) + return; + + /* After we have deleted an interface from ovsdb, the link will stay + * in platform until ovs-vswitch removes it. To avoid race conditions, + * we need to wait until the link goes away; otherwise, after adding the + * interface again, these race conditions can happen: + * 1) we see the link in platform, and proceed with activation. But after + * that, ovs-vswitchd reads the updates from ovsdb-server and deletes/recreates + * the link. + * 2) ovs-vswitch combines the delete/insert of the interface to a no-op. NM sees + * the link staying in platform, but doesn't know whether the link is ready + * or we are again in case 1) + * In other words, it's necessary to wait that the link goes away before inserting + * the interface again. + */ + while (i < nm_g_ptr_array_len(priv->cleanup.interfaces)) { + const char *ifname; + const NMDedupMultiHeadEntry *pl_links_head_entry; + NMDedupMultiIter pliter; + const NMPlatformLink *link; + gboolean found = FALSE; + + ifname = priv->cleanup.interfaces->pdata[i]; + pl_links_head_entry = nm_platform_lookup_link_by_ifname(priv->platform, ifname); + nmp_cache_iter_for_each_link (&pliter, pl_links_head_entry, &link) { + if (link->type == NM_LINK_TYPE_OPENVSWITCH + && nmp_object_is_visible(NMP_OBJECT_UP_CAST(link))) { + found = TRUE; + break; + } + } + + if (!found) { + g_ptr_array_remove_index_fast(priv->cleanup.interfaces, i); + continue; + } + i++; } + + if (nm_g_ptr_array_len(priv->cleanup.interfaces) == 0) { + cleanup_emit_ready(self, "all interfaces deleted"); + return; + } + + _LOGT("cleanup: still waiting for %d interfaces", priv->cleanup.interfaces->len); + + if (priv->cleanup.timeout_source) { + /* We already registered the timeout/change-callback */ + return; + } + + priv->cleanup.timeout_source = + nm_g_timeout_add_seconds_source(6, G_SOURCE_FUNC(cleanup_timeout), self); + priv->cleanup.link_changed_id = g_signal_connect(priv->platform, + NM_PLATFORM_SIGNAL_LINK_CHANGED, + G_CALLBACK(cleanup_link_cb), + self); } static void -_del_initial_iface_cb(GError *error, gpointer user_data) +cleanup_del_iface_cb(GError *error, gpointer user_data) { NMOvsdb *self; gs_free char *ifname = NULL; @@ -2309,18 +2411,18 @@ _del_initial_iface_cb(GError *error, gpointer user_data) return; priv = NM_OVSDB_GET_PRIVATE(self); - nm_assert(priv->num_pending_deletions > 0); - priv->num_pending_deletions--; + nm_assert(priv->cleanup.num_pending_del > 0); + priv->cleanup.num_pending_del--; - _LOGD("delete initial interface '%s': %s %s%s%s, pending %u", + _LOGD("cleanup: deleted interface '%s': %s %s%s%s, pending %u", ifname, error ? "error" : "success", error ? "(" : "", error ? error->message : "", error ? ")" : "", - priv->num_pending_deletions); + priv->cleanup.num_pending_del); - _check_ready(self); + cleanup_check_ready(self); } static void @@ -2331,7 +2433,7 @@ ovsdb_cleanup_initial_interfaces(NMOvsdb *self) NMUtilsUserData *data; GHashTableIter iter; - if (priv->ready || priv->num_pending_deletions != 0) + if (priv->ready || priv->cleanup.num_pending_del > 0 || priv->cleanup.interfaces) return; /* Delete OVS interfaces added by NM. Bridges and ports and @@ -2339,17 +2441,22 @@ ovsdb_cleanup_initial_interfaces(NMOvsdb *self) * when no interface is present. */ g_hash_table_iter_init(&iter, self->_priv.interfaces); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &interface)) { - if (interface->connection_uuid) { - priv->num_pending_deletions++; - _LOGD("deleting initial interface '%s' (pending: %u)", - interface->name, - priv->num_pending_deletions); - data = nm_utils_user_data_pack(self, g_strdup(interface->name)); - nm_ovsdb_del_interface(self, interface->name, _del_initial_iface_cb, data); + if (!interface->connection_uuid) { + /* not created by NM, ignore */ + continue; } + + if (!priv->cleanup.interfaces) + priv->cleanup.interfaces = g_ptr_array_new_with_free_func(g_free); + g_ptr_array_add(priv->cleanup.interfaces, g_strdup(interface->name)); + + _LOGD("cleanup: deleting interface '%s'", interface->name); + priv->cleanup.num_pending_del++; + data = nm_utils_user_data_pack(self, g_strdup(interface->name)); + nm_ovsdb_del_interface(self, interface->name, cleanup_del_iface_cb, data); } - _check_ready(self); + cleanup_check_ready(self); } static void @@ -2622,8 +2729,9 @@ nm_ovsdb_init(NMOvsdb *self) c_list_init(&priv->calls_lst_head); - priv->input = g_string_new(NULL); - priv->output = g_string_new(NULL); + 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 = @@ -2653,6 +2761,7 @@ dispose(GObject *object) priv->output = NULL; } + g_clear_object(&priv->platform); nm_clear_pointer(&priv->bridges, g_hash_table_destroy); nm_clear_pointer(&priv->ports, g_hash_table_destroy); nm_clear_pointer(&priv->interfaces, g_hash_table_destroy); |