summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-12-17 18:44:43 +0100
committerThomas Haller <thaller@redhat.com>2015-12-17 18:44:43 +0100
commit5aba5685f2a7bf227ed923d969be29ce8d543b9a (patch)
tree2baff7ea70f2446375f2400ba2642165c094e6fc
parent21efcfe6b57ffc36e0e7a89ee4dcb10b35ee6827 (diff)
parentc40acf334e90e77df5f6a0641d378812b552d518 (diff)
downloadNetworkManager-5aba5685f2a7bf227ed923d969be29ce8d543b9a.tar.gz
platform: merge branch 'th/platform-no-sync-socket-bgo759490'
https://bugzilla.gnome.org/show_bug.cgi?id=759490
-rw-r--r--src/nm-multi-index.c154
-rw-r--r--src/nm-multi-index.h5
-rw-r--r--src/platform/nm-linux-platform.c1489
-rw-r--r--src/platform/nmp-object.c47
-rw-r--r--src/platform/nmp-object.h9
-rw-r--r--src/platform/tests/test-link.c10
6 files changed, 1022 insertions, 692 deletions
diff --git a/src/nm-multi-index.c b/src/nm-multi-index.c
index da3507d0b3..47cd981055 100644
--- a/src/nm-multi-index.c
+++ b/src/nm-multi-index.c
@@ -34,56 +34,72 @@ struct NMMultiIndex {
};
typedef struct {
+ /* when storing the first item for a multi-index id, we don't yet create
+ * the hashtable @index. Instead we store it inplace to @value0. Note that
+ * &values_data->value0 is a NULL terminated array with one item that is
+ * suitable to be returned directly from nm_multi_index_lookup(). */
+ union {
+ gpointer value0;
+ gpointer *values;
+ };
GHashTable *index;
- gpointer *values;
} ValuesData;
/******************************************************************************************/
-static ValuesData *
-_values_data_create (void)
-{
- ValuesData *values_data;
-
- values_data = g_slice_new (ValuesData);
- values_data->index = g_hash_table_new (NULL, NULL);
- values_data->values = NULL;
- return values_data;
-}
-
static void
_values_data_destroy (ValuesData *values_data)
{
- if (values_data) {
+ if (values_data->index) {
g_free (values_data->values);
g_hash_table_unref (values_data->index);
- g_slice_free (ValuesData, values_data);
}
+ g_slice_free (ValuesData, values_data);
+}
+
+static gboolean
+_values_data_contains (ValuesData *values_data, gconstpointer value)
+{
+ return values_data->index
+ ? g_hash_table_contains (values_data->index, value)
+ : value == values_data->value0;
}
static void
-_values_data_populate_array (ValuesData *values_data)
+_values_data_get_data (ValuesData *values_data,
+ void *const**out_data,
+ guint *out_len)
{
guint i, len;
gpointer *values;
GHashTableIter iter;
nm_assert (values_data);
- nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0);
- if (values_data->values)
+ if (!values_data->index) {
+ NM_SET_OUT (out_data, &values_data->value0);
+ NM_SET_OUT (out_len, 1);
return;
+ }
- len = g_hash_table_size (values_data->index);
- values = g_new (gpointer, len + 1);
+ nm_assert (values_data->index && g_hash_table_size (values_data->index) > 0);
+
+ if (!values_data->values) {
+ len = g_hash_table_size (values_data->index);
+ values = g_new (gpointer, len + 1);
- g_hash_table_iter_init (&iter, values_data->index);
- for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++)
- nm_assert (i < len);
- nm_assert (i == len);
- values[i] = NULL;
+ g_hash_table_iter_init (&iter, values_data->index);
+ for (i = 0; g_hash_table_iter_next (&iter, &values[i], NULL); i++)
+ nm_assert (i < len);
+ nm_assert (i == len);
+ values[i] = NULL;
- values_data->values = values;
+ values_data->values = values;
+ NM_SET_OUT (out_len, len);
+ } else if (out_len)
+ NM_SET_OUT (out_len, g_hash_table_size (values_data->index));
+
+ NM_SET_OUT (out_data, values_data->values);
}
/******************************************************************************************/
@@ -104,6 +120,7 @@ nm_multi_index_lookup (const NMMultiIndex *index,
guint *out_len)
{
ValuesData *values_data;
+ void *const*values;
g_return_val_if_fail (index, NULL);
g_return_val_if_fail (id, NULL);
@@ -114,10 +131,8 @@ nm_multi_index_lookup (const NMMultiIndex *index,
*out_len = 0;
return NULL;
}
- _values_data_populate_array (values_data);
- if (out_len)
- *out_len = g_hash_table_size (values_data->index);
- return values_data->values;
+ _values_data_get_data (values_data, &values, out_len);
+ return values;
}
gboolean
@@ -132,8 +147,7 @@ nm_multi_index_contains (const NMMultiIndex *index,
g_return_val_if_fail (value, FALSE);
values_data = g_hash_table_lookup (index->hash, id);
- return values_data
- && g_hash_table_contains (values_data->index, value);
+ return values_data && _values_data_contains (values_data, value);
}
const NMMultiIndexId *
@@ -157,7 +171,7 @@ nm_multi_index_lookup_first_by_value (const NMMultiIndex *index,
g_hash_table_iter_init (&iter, index->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) {
- if (g_hash_table_contains (values_data->index, value))
+ if (_values_data_contains (values_data, value))
return id;
}
return NULL;
@@ -172,18 +186,19 @@ nm_multi_index_foreach (const NMMultiIndex *index,
GHashTableIter iter;
const NMMultiIndexId *id;
ValuesData *values_data;
+ guint len;
+ void *const*values;
g_return_if_fail (index);
g_return_if_fail (foreach_func);
g_hash_table_iter_init (&iter, index->hash);
while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &values_data)) {
- if ( value
- && !g_hash_table_contains (values_data->index, value))
+ if (value && !_values_data_contains (values_data, value))
continue;
- _values_data_populate_array (values_data);
- if (!foreach_func (id, values_data->values, g_hash_table_size (values_data->index), user_data))
+ _values_data_get_data (values_data, &values, &len);
+ if (!foreach_func (id, values, len, user_data))
return;
}
}
@@ -214,14 +229,11 @@ nm_multi_index_iter_next (NMMultiIndexIter *iter,
while (g_hash_table_iter_next (&iter->_iter, (gpointer *) &id, (gpointer *) &values_data)) {
if ( !iter->_value
- || g_hash_table_contains (values_data->index, iter->_value)) {
- _values_data_populate_array (values_data);
+ || _values_data_contains (values_data, iter->_value)) {
+ if (out_values || out_len)
+ _values_data_get_data (values_data, out_values, out_len);
if (out_id)
*out_id = id;
- if (out_values)
- *out_values = values_data->values;
- if (out_len)
- *out_len = g_hash_table_size (values_data->index);
return TRUE;
}
}
@@ -243,10 +255,13 @@ nm_multi_index_id_iter_init (NMMultiIndexIdIter *iter,
values_data = g_hash_table_lookup (index->hash, id);
if (!values_data)
+ iter->_state = 2;
+ else if (values_data->index) {
iter->_state = 1;
- else {
- iter->_state = 0;
g_hash_table_iter_init (&iter->_iter, values_data->index);
+ } else {
+ iter->_state = 0;
+ iter->_value = values_data->value0;
}
}
@@ -255,13 +270,19 @@ nm_multi_index_id_iter_next (NMMultiIndexIdIter *iter,
void **out_value)
{
g_return_val_if_fail (iter, FALSE);
- g_return_val_if_fail (iter->_state <= 1, FALSE);
- if (iter->_state == 0)
- return g_hash_table_iter_next (&iter->_iter, out_value, NULL);
- else {
+ switch (iter->_state) {
+ case 0:
iter->_state = 2;
+ NM_SET_OUT (out_value, iter->_value);
+ return TRUE;
+ case 1:
+ return g_hash_table_iter_next (&iter->_iter, out_value, NULL);
+ case 2:
+ iter->_state = 3;
return FALSE;
+ default:
+ g_return_val_if_reached (FALSE);
}
}
@@ -291,14 +312,23 @@ _do_add (NMMultiIndex *index,
if (!id_new)
g_return_val_if_reached (FALSE);
- values_data = _values_data_create ();
- g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value);
+ values_data = g_slice_new0 (ValuesData);
+ values_data->value0 = (gpointer) value;
g_hash_table_insert (index->hash, id_new, values_data);
} else {
- if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value))
- return FALSE;
- g_clear_pointer (&values_data->values, g_free);
+ if (!values_data->index) {
+ if (values_data->value0 == value)
+ return FALSE;
+ values_data->index = g_hash_table_new (NULL, NULL);
+ g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value);
+ g_hash_table_replace (values_data->index, values_data->value0, values_data->value0);
+ values_data->values = NULL;
+ } else {
+ if (!nm_g_hash_table_replace (values_data->index, (gpointer) value, (gpointer) value))
+ return FALSE;
+ g_clear_pointer (&values_data->values, g_free);
+ }
}
return TRUE;
}
@@ -314,13 +344,19 @@ _do_remove (NMMultiIndex *index,
if (!values_data)
return FALSE;
- if (!g_hash_table_remove (values_data->index, value))
- return FALSE;
-
- if (g_hash_table_size (values_data->index) == 0)
+ if (values_data->index) {
+ if (!g_hash_table_remove (values_data->index, value))
+ return FALSE;
+ if (g_hash_table_size (values_data->index) == 0)
+ g_hash_table_remove (index->hash, id);
+ else
+ g_clear_pointer (&values_data->values, g_free);
+ } else {
+ if (values_data->value0 != value)
+ return FALSE;
g_hash_table_remove (index->hash, id);
- else
- g_clear_pointer (&values_data->values, g_free);
+ }
+
return TRUE;
}
diff --git a/src/nm-multi-index.h b/src/nm-multi-index.h
index 046e2431b8..1e1e8fdaee 100644
--- a/src/nm-multi-index.h
+++ b/src/nm-multi-index.h
@@ -39,7 +39,10 @@ typedef struct {
} NMMultiIndexIter;
typedef struct {
- GHashTableIter _iter;
+ union {
+ GHashTableIter _iter;
+ gpointer _value;
+ };
guint _state;
} NMMultiIndexIdIter;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 93a8be9f6a..fd96d66695 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -154,6 +154,7 @@ typedef enum {
DELAYED_ACTION_TYPE_REFRESH_LINK = (1LL << 5),
DELAYED_ACTION_TYPE_MASTER_CONNECTED = (1LL << 6),
DELAYED_ACTION_TYPE_READ_NETLINK = (1LL << 7),
+ DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE = (1LL << 8),
__DELAYED_ACTION_TYPE_MAX,
DELAYED_ACTION_TYPE_REFRESH_ALL = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS |
@@ -165,12 +166,59 @@ typedef enum {
DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1,
} DelayedActionType;
+typedef enum {
+ /* Negative values are errors from kernel. Add dummy member to
+ * make enum signed. */
+ _WAIT_FOR_NL_RESPONSE_RESULT_SYSTEM_ERROR = -1,
+
+ WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN = 0,
+ WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK,
+ WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN,
+ WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC,
+ WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL,
+ WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT,
+ WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING,
+} WaitForNlResponseResult;
+
+typedef void (*WaitForNlResponseCallback) (NMPlatform *platform,
+ guint32 seq_number,
+ WaitForNlResponseResult seq_result,
+ gpointer user_data);
+
static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data);
static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink);
-static void do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action);
-static void do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action);
+static void do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name);
+static void do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type);
static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data);
-static gboolean event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks);
+static void cache_prune_candidates_prune (NMPlatform *platform);
+static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks);
+
+/*****************************************************************************/
+
+static const char *
+wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size)
+{
+ char *buf0 = buf;
+
+ switch (seq_result) {
+ case WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN:
+ nm_utils_strbuf_append_str (&buf, &buf_size, "unknown");
+ break;
+ case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK:
+ nm_utils_strbuf_append_str (&buf, &buf_size, "success");
+ break;
+ case WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN:
+ nm_utils_strbuf_append_str (&buf, &buf_size, "failure");
+ break;
+ default:
+ if (seq_result < 0)
+ nm_utils_strbuf_append (&buf, &buf_size, "failure %d (%s)", -((int) seq_result), g_strerror (-((int) seq_result)));
+ else
+ nm_utils_strbuf_append (&buf, &buf_size, "internal failure %d", (int) seq_result);
+ break;
+ }
+ return buf0;
+}
/******************************************************************
* Support IFLA_INET6_ADDR_GEN_MODE
@@ -2249,38 +2297,6 @@ nla_put_failure:
/******************************************************************/
-static int
-_nl_sock_flush_data (struct nl_sock *sk)
-{
- int nle;
- struct nl_cb *cb;
- struct nl_cb *cb0;
-
- cb0 = nl_socket_get_cb (sk);
- cb = nl_cb_clone (cb0);
- nl_cb_put (cb0);
- if (cb == NULL)
- return -NLE_NOMEM;
-
- nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL);
- nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_DEFAULT, NULL, NULL);
- nl_cb_err (cb, NL_CB_DEFAULT, NULL, NULL);
- do {
- errno = 0;
-
- nle = nl_recvmsgs (sk, cb);
-
- /* Work around a libnl bug fixed in 3.2.22 (375a6294) */
- if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
- nle = -NLE_AGAIN;
- } while (nle != -NLE_AGAIN);
-
- nl_cb_put (cb);
- return nle;
-}
-
-/******************************************************************/
-
static int _support_kernel_extended_ifa_flags = -1;
#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1))
@@ -2323,13 +2339,19 @@ _support_kernel_extended_ifa_flags_get (void)
* NMPlatform types and functions
******************************************************************/
+typedef struct {
+ guint32 seq_number;
+ gint64 timeout_abs_ns;
+ WaitForNlResponseResult seq_result;
+ WaitForNlResponseResult *out_seq_result;
+} DelayedActionWaitForNlResponseData;
+
typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate;
struct _NMLinuxPlatformPrivate {
struct nl_sock *nlh;
- struct nl_sock *nlh_event;
- guint32 nlh_seq_expect;
- guint32 nlh_seq_last;
+ guint32 nlh_seq_next;
+ guint32 nlh_seq_last_handled;
NMPCache *cache;
GIOChannel *event_channel;
guint event_id;
@@ -2343,8 +2365,8 @@ struct _NMLinuxPlatformPrivate {
DelayedActionType flags;
GPtrArray *list_master_connected;
GPtrArray *list_refresh_link;
+ GArray *list_wait_for_nl_response;
gint is_handling;
- guint idle_id;
} delayed_action;
GHashTable *prune_candidates;
@@ -2372,6 +2394,188 @@ nm_linux_platform_setup (void)
/******************************************************************/
+static void
+_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
+{
+ GError *error = NULL;
+ char *contents, *contents_escaped;
+ char *value_escaped = g_strescape (value, NULL);
+
+ if (!g_file_get_contents (path, &contents, NULL, &error)) {
+ _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message);
+ g_clear_error (&error);
+ } else {
+ g_strstrip (contents);
+ contents_escaped = g_strescape (contents, NULL);
+ if (strcmp (contents, value) == 0)
+ _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped);
+ else
+ _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped);
+ g_free (contents);
+ g_free (contents_escaped);
+ }
+ g_free (value_escaped);
+}
+
+#define _log_dbg_sysctl_set(platform, path, value) \
+ G_STMT_START { \
+ if (_LOGD_ENABLED ()) { \
+ _log_dbg_sysctl_set_impl (platform, path, value); \
+ } \
+ } G_STMT_END
+
+static gboolean
+sysctl_set (NMPlatform *platform, const char *path, const char *value)
+{
+ int fd, len, nwrote, tries;
+ char *actual;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ /* Don't write outside known locations */
+ g_assert (g_str_has_prefix (path, "/proc/sys/")
+ || g_str_has_prefix (path, "/sys/"));
+ /* Don't write to suspicious locations */
+ g_assert (!strstr (path, "/../"));
+
+ fd = open (path, O_WRONLY | O_TRUNC);
+ if (fd == -1) {
+ if (errno == ENOENT) {
+ _LOGD ("sysctl: failed to open '%s': (%d) %s",
+ path, errno, strerror (errno));
+ } else {
+ _LOGE ("sysctl: failed to open '%s': (%d) %s",
+ path, errno, strerror (errno));
+ }
+ return FALSE;
+ }
+
+ _log_dbg_sysctl_set (platform, path, value);
+
+ /* Most sysfs and sysctl options don't care about a trailing LF, while some
+ * (like infiniband) do. So always add the LF. Also, neither sysfs nor
+ * sysctl support partial writes so the LF must be added to the string we're
+ * about to write.
+ */
+ actual = g_strdup_printf ("%s\n", value);
+
+ /* Try to write the entire value three times if a partial write occurs */
+ len = strlen (actual);
+ for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
+ nwrote = write (fd, actual, len);
+ if (nwrote == -1) {
+ if (errno == EINTR) {
+ _LOGD ("sysctl: interrupted, will try again");
+ continue;
+ }
+ break;
+ }
+ }
+ if (nwrote == -1 && errno != EEXIST) {
+ _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s",
+ path, value, errno, strerror (errno));
+ } else if (nwrote < len) {
+ _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts",
+ path, value);
+ }
+
+ g_free (actual);
+ close (fd);
+ return (nwrote == len);
+}
+
+static GSList *sysctl_clear_cache_list;
+
+void
+_nm_linux_platform_sysctl_clear_cache (void)
+{
+ while (sysctl_clear_cache_list) {
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (sysctl_clear_cache_list->data);
+
+ sysctl_clear_cache_list = g_slist_delete_link (sysctl_clear_cache_list, sysctl_clear_cache_list);
+
+ g_hash_table_destroy (priv->sysctl_get_prev_values);
+ priv->sysctl_get_prev_values = NULL;
+ priv->sysctl_get_warned = FALSE;
+ }
+}
+
+static void
+_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *contents)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ const char *prev_value = NULL;
+
+ if (!priv->sysctl_get_prev_values) {
+ sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform);
+ priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ } else
+ prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, path);
+
+ if (prev_value) {
+ if (strcmp (prev_value, contents) != 0) {
+ char *contents_escaped = g_strescape (contents, NULL);
+ char *prev_value_escaped = g_strescape (prev_value, NULL);
+
+ _LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped);
+ g_free (contents_escaped);
+ g_free (prev_value_escaped);
+ g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
+ }
+ } else {
+ char *contents_escaped = g_strescape (contents, NULL);
+
+ _LOGD ("sysctl: reading '%s': '%s'", path, contents_escaped);
+ g_free (contents_escaped);
+ g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
+ }
+
+ if ( !priv->sysctl_get_warned
+ && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
+ _LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
+ priv->sysctl_get_warned = TRUE;
+ }
+}
+
+#define _log_dbg_sysctl_get(platform, path, contents) \
+ G_STMT_START { \
+ if (_LOGD_ENABLED ()) \
+ _log_dbg_sysctl_get_impl (platform, path, contents); \
+ } G_STMT_END
+
+static char *
+sysctl_get (NMPlatform *platform, const char *path)
+{
+ GError *error = NULL;
+ char *contents;
+
+ /* Don't write outside known locations */
+ g_assert (g_str_has_prefix (path, "/proc/sys/")
+ || g_str_has_prefix (path, "/sys/"));
+ /* Don't write to suspicious locations */
+ g_assert (!strstr (path, "/../"));
+
+ if (!g_file_get_contents (path, &contents, NULL, &error)) {
+ /* We assume FAILED means EOPNOTSUP */
+ if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
+ || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED))
+ _LOGD ("error reading %s: %s", path, error->message);
+ else
+ _LOGE ("error reading %s: %s", path, error->message);
+ g_clear_error (&error);
+ return NULL;
+ }
+
+ g_strstrip (contents);
+
+ _log_dbg_sysctl_get (platform, path, contents);
+
+ return contents;
+}
+
+/******************************************************************/
+
static gboolean
check_support_kernel_extended_ifa_flags (NMPlatform *platform)
{
@@ -2508,13 +2712,105 @@ delayed_action_to_string (DelayedActionType action_type)
case DELAYED_ACTION_TYPE_REFRESH_LINK : return "refresh-link";
case DELAYED_ACTION_TYPE_MASTER_CONNECTED : return "master-connected";
case DELAYED_ACTION_TYPE_READ_NETLINK : return "read-netlink";
+ case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE : return "wait-for-nl-response";
default:
return "unknown";
}
}
-#define _LOGt_delayed_action(action_type, arg, operation) \
- _LOGt ("delayed-action: %s %s (%d) [%p / %d]", ""operation, delayed_action_to_string (action_type), (int) action_type, arg, GPOINTER_TO_INT (arg))
+static const char *
+delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data, char *buf, gsize buf_size)
+{
+ char *buf0 = buf;
+ const DelayedActionWaitForNlResponseData *data;
+
+ nm_utils_strbuf_append_str (&buf, &buf_size, delayed_action_to_string (action_type));
+ switch (action_type) {
+ case DELAYED_ACTION_TYPE_MASTER_CONNECTED:
+ nm_utils_strbuf_append (&buf, &buf_size, " (master-ifindex %d)", GPOINTER_TO_INT (user_data));
+ break;
+ case DELAYED_ACTION_TYPE_REFRESH_LINK:
+ nm_utils_strbuf_append (&buf, &buf_size, " (ifindex %d)", GPOINTER_TO_INT (user_data));
+ break;
+ case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE:
+ data = user_data;
+
+ if (data) {
+ gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns ();
+ char b[255];
+
+ nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%ld.%09ld%s%s)",
+ data->seq_number,
+ timeout < 0 ? "-" : "",
+ (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND,
+ (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND,
+ data->seq_result ? ", " : "",
+ data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : "");
+ } else
+ nm_utils_strbuf_append_str (&buf, &buf_size, " (any)");
+ break;
+ default:
+ nm_assert (!user_data);
+ break;
+ }
+ return buf0;
+}
+
+#define _LOGt_delayed_action(action_type, user_data, operation) \
+ G_STMT_START { \
+ char _buf[255]; \
+ \
+ _LOGt ("delayed-action: %s %s", \
+ ""operation, \
+ delayed_action_to_string_full (action_type, user_data, _buf, sizeof (_buf))); \
+ } G_STMT_END
+
+/*****************************************************************************/
+
+static void
+delayed_action_wait_for_nl_response_complete (NMPlatform *platform,
+ guint idx,
+ WaitForNlResponseResult seq_result)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ DelayedActionWaitForNlResponseData *data;
+ WaitForNlResponseResult *out_seq_result;
+
+ nm_assert (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
+ nm_assert (idx < priv->delayed_action.list_wait_for_nl_response->len);
+ nm_assert (seq_result);
+
+ data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, idx);
+
+ _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, data, "complete");
+
+ out_seq_result = data->out_seq_result;
+
+ g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx);
+ /* Note: @data is invalidated at this point */
+
+ if (priv->delayed_action.list_wait_for_nl_response->len <= 0)
+ priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE;
+
+ if (out_seq_result)
+ *out_seq_result = seq_result;
+}
+
+static void
+delayed_action_wait_for_nl_response_complete_all (NMPlatform *platform,
+ WaitForNlResponseResult result)
+{
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+
+ if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
+ while (priv->delayed_action.list_wait_for_nl_response->len > 0)
+ delayed_action_wait_for_nl_response_complete (platform, priv->delayed_action.list_wait_for_nl_response->len - 1, result);
+ }
+ nm_assert (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
+ nm_assert (priv->delayed_action.list_wait_for_nl_response->len == 0);
+}
+
+/*****************************************************************************/
static void
delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex)
@@ -2531,19 +2827,25 @@ delayed_action_handle_MASTER_CONNECTED (NMPlatform *platform, int master_ifindex
static void
delayed_action_handle_REFRESH_LINK (NMPlatform *platform, int ifindex)
{
- do_request_link (platform, ifindex, NULL, FALSE);
+ do_request_link_no_delayed_actions (platform, ifindex, NULL);
}
static void
delayed_action_handle_REFRESH_ALL (NMPlatform *platform, DelayedActionType flags)
{
- do_request_all (platform, flags, FALSE);
+ do_request_all_no_delayed_actions (platform, flags);
}
static void
delayed_action_handle_READ_NETLINK (NMPlatform *platform)
{
- event_handler_read_netlink_all (platform, TRUE);
+ event_handler_read_netlink (platform, FALSE);
+}
+
+static void
+delayed_action_handle_WAIT_FOR_NL_RESPONSE (NMPlatform *platform)
+{
+ event_handler_read_netlink (platform, TRUE);
}
static gboolean
@@ -2552,10 +2854,8 @@ delayed_action_handle_one (NMPlatform *platform)
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
gpointer user_data;
- if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE) {
- nm_clear_g_source (&priv->delayed_action.idle_id);
+ if (priv->delayed_action.flags == DELAYED_ACTION_TYPE_NONE)
return FALSE;
- }
/* First process DELAYED_ACTION_TYPE_MASTER_CONNECTED actions.
* This type of action is entirely cache-internal and is here to resolve a
@@ -2602,20 +2902,30 @@ delayed_action_handle_one (NMPlatform *platform)
return TRUE;
}
- nm_assert (priv->delayed_action.flags == DELAYED_ACTION_TYPE_REFRESH_LINK);
- nm_assert (priv->delayed_action.list_refresh_link->len > 0);
+ if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_REFRESH_LINK)) {
+ nm_assert (priv->delayed_action.list_refresh_link->len > 0);
- user_data = priv->delayed_action.list_refresh_link->pdata[0];
- g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, 0);
- if (priv->delayed_action.list_refresh_link->len == 0)
- priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
- nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0);
+ user_data = priv->delayed_action.list_refresh_link->pdata[0];
+ g_ptr_array_remove_index_fast (priv->delayed_action.list_refresh_link, 0);
+ if (priv->delayed_action.list_refresh_link->len == 0)
+ priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_REFRESH_LINK;
+ nm_assert (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0);
- _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle");
+ _LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, user_data, "handle");
- delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data));
+ delayed_action_handle_REFRESH_LINK (platform, GPOINTER_TO_INT (user_data));
- return TRUE;
+ return TRUE;
+ }
+
+ if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
+ nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0);
+ _LOGt_delayed_action (DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE, NULL, "handle");
+ delayed_action_handle_WAIT_FOR_NL_RESPONSE (platform);
+ return TRUE;
+ }
+
+ return FALSE;
}
static gboolean
@@ -2624,22 +2934,18 @@ delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink)
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
gboolean any = FALSE;
- nm_clear_g_source (&priv->delayed_action.idle_id);
+ g_return_val_if_fail (priv->delayed_action.is_handling == 0, FALSE);
+
priv->delayed_action.is_handling++;
if (read_netlink)
delayed_action_schedule (platform, DELAYED_ACTION_TYPE_READ_NETLINK, NULL);
while (delayed_action_handle_one (platform))
any = TRUE;
priv->delayed_action.is_handling--;
- return any;
-}
-static gboolean
-delayed_action_handle_idle (gpointer user_data)
-{
- NM_LINUX_PLATFORM_GET_PRIVATE (user_data)->delayed_action.idle_id = 0;
- delayed_action_handle_all (user_data, FALSE);
- return G_SOURCE_REMOVE;
+ cache_prune_candidates_prune (platform);
+
+ return any;
}
static void
@@ -2650,16 +2956,25 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp
nm_assert (action_type != DELAYED_ACTION_TYPE_NONE);
- if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK)) {
- nm_assert (nm_utils_is_power_of_two (action_type));
+ switch (action_type) {
+ case DELAYED_ACTION_TYPE_REFRESH_LINK:
if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_refresh_link->pdata, priv->delayed_action.list_refresh_link->len, user_data) < 0)
g_ptr_array_add (priv->delayed_action.list_refresh_link, user_data);
- } else if (NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED)) {
- nm_assert (nm_utils_is_power_of_two (action_type));
+ break;
+ case DELAYED_ACTION_TYPE_MASTER_CONNECTED:
if (_nm_utils_ptrarray_find_first (priv->delayed_action.list_master_connected->pdata, priv->delayed_action.list_master_connected->len, user_data) < 0)
g_ptr_array_add (priv->delayed_action.list_master_connected, user_data);
- } else
+ break;
+ case DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE:
+ g_array_append_vals (priv->delayed_action.list_wait_for_nl_response, user_data, 1);
+ break;
+ default:
nm_assert (!user_data);
+ nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_REFRESH_LINK));
+ nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_MASTER_CONNECTED));
+ nm_assert (!NM_FLAGS_HAS (action_type, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE));
+ break;
+ }
priv->delayed_action.flags |= action_type;
@@ -2669,9 +2984,22 @@ delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gp
_LOGt_delayed_action (iflags, user_data, "schedule");
}
}
+}
+
+static void
+delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform,
+ guint32 seq_number,
+ WaitForNlResponseResult *out_seq_result)
+{
+ DelayedActionWaitForNlResponseData data = {
+ .seq_number = seq_number,
+ .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)),
+ .out_seq_result = out_seq_result,
+ };
- if (priv->delayed_action.is_handling == 0 && priv->delayed_action.idle_id == 0)
- priv->delayed_action.idle_id = g_idle_add (delayed_action_handle_idle, platform);
+ delayed_action_schedule (platform,
+ DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE,
+ &data);
}
/******************************************************************/
@@ -2966,38 +3294,31 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP
/******************************************************************/
static int
-_nl_send_auto_with_seq (NMPlatform *platform, struct nl_msg *nlmsg)
+_nl_send_auto_with_seq (NMPlatform *platform,
+ struct nl_msg *nlmsg,
+ WaitForNlResponseResult *out_seq_result)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
guint32 seq;
int nle;
- /* complete the message, by choosing our own sequence number, because libnl
- * does not ensure that it isn't zero -- which would confuse our checking for
- * outstanding messages. */
- seq = nl_socket_use_seq (priv->nlh_event);
- if (seq == 0)
- seq = nl_socket_use_seq (priv->nlh_event);
+ /* complete the message with a sequence number (ensuring it's not zero). */
+ seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++;
nlmsg_hdr (nlmsg)->nlmsg_seq = seq;
- nle = nl_send_auto (priv->nlh_event, nlmsg);
+ nle = nl_send_auto (priv->nlh, nlmsg);
- if (nle >= 0) {
- _LOGt ("sequence-number: new %u%s",
- seq,
- priv->nlh_seq_expect
- ? nm_sprintf_bufa (100, " (replaces %u)", priv->nlh_seq_expect)
- : "");
- priv->nlh_seq_expect = seq;
- } else
- _LOGD ("failed sending message: %s (%d)", nl_geterror (nle), nle);
+ if (nle >= 0)
+ delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result);
+ else
+ _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle);
return nle;
}
static void
-do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action)
+do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const char *name)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
@@ -3014,7 +3335,7 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean h
(NMPObject *) nmp_cache_lookup_link (priv->cache, ifindex));
}
- event_handler_read_netlink_all (platform, FALSE);
+ event_handler_read_netlink (platform, FALSE);
nlmsg = _nl_msg_new_link (RTM_GETLINK,
0,
@@ -3023,24 +3344,18 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean h
0,
0);
if (nlmsg)
- _nl_send_auto_with_seq (platform, nlmsg);
-
- event_handler_read_netlink_all (platform, TRUE);
-
- cache_prune_candidates_prune (platform);
-
- if (handle_delayed_action)
- delayed_action_handle_all (platform, FALSE);
+ _nl_send_auto_with_seq (platform, nlmsg, NULL);
}
static void
-do_request_one_type (NMPlatform *platform, NMPObjectType obj_type, gboolean handle_delayed_action)
+do_request_link (NMPlatform *platform, int ifindex, const char *name)
{
- do_request_all (platform, delayed_action_refresh_from_object_type (obj_type), handle_delayed_action);
+ do_request_link_no_delayed_actions (platform, ifindex, name);
+ delayed_action_handle_all (platform, FALSE);
}
static void
-do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action)
+do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType action_type)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
DelayedActionType iflags;
@@ -3072,7 +3387,7 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha
_LOGt_delayed_action (DELAYED_ACTION_TYPE_REFRESH_LINK, NULL, "clear (do-request-all)");
}
- event_handler_read_netlink_all (platform, FALSE);
+ event_handler_read_netlink (platform, FALSE);
/* reimplement
* nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP);
@@ -3086,66 +3401,62 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha
if (nle < 0)
goto next;
- _nl_send_auto_with_seq (platform, nlmsg);
+ _nl_send_auto_with_seq (platform, nlmsg, NULL);
}
next:
;
}
- event_handler_read_netlink_all (platform, TRUE);
-
- cache_prune_candidates_prune (platform);
+}
- if (handle_delayed_action)
- delayed_action_handle_all (platform, FALSE);
+static void
+do_request_one_type (NMPlatform *platform, NMPObjectType obj_type)
+{
+ do_request_all_no_delayed_actions (platform, delayed_action_refresh_from_object_type (obj_type));
+ delayed_action_handle_all (platform, FALSE);
}
-static int
-event_seq_check (struct nl_msg *msg, gpointer user_data)
+static void
+event_seq_check (NMPlatform *platform, struct nl_msg *msg, WaitForNlResponseResult seq_result)
{
- NMPlatform *platform = NM_PLATFORM (user_data);
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- struct nlmsghdr *hdr;
-
- hdr = nlmsg_hdr (msg);
-
- if (hdr->nlmsg_seq == 0)
- return NL_OK;
-
- priv->nlh_seq_last = hdr->nlmsg_seq;
+ DelayedActionWaitForNlResponseData *data;
+ guint32 seq_number;
+ guint i;
- if (priv->nlh_seq_expect == 0)
- _LOGt ("sequence-number: seq %u received (not waited)", hdr->nlmsg_seq);
- else if (hdr->nlmsg_seq == priv->nlh_seq_expect) {
- _LOGt ("sequence-number: seq %u received", hdr->nlmsg_seq);
+ seq_number = nlmsg_hdr (msg)->nlmsg_seq;
- priv->nlh_seq_expect = 0;
- } else
- _LOGt ("sequence-number: seq %u received (wait for %u)", hdr->nlmsg_seq, priv->nlh_seq_last);
+ if (seq_number == 0)
+ return;
- return NL_OK;
-}
+ if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) {
+ nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0);
+
+ for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) {
+ data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i);
+
+ if (data->seq_number == seq_number) {
+ /* We potentially receive many parts partial responses for the same sequence number.
+ * Thus, we only remember the result, and collect it later. */
+ if (data->seq_result < 0) {
+ /* we already saw an error for this seqence number.
+ * Preserve it. */
+ } else if ( seq_result != WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN
+ || data->seq_result == WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN)
+ data->seq_result = seq_result;
+ return;
+ }
+ }
+ }
-static int
-event_err (struct sockaddr_nl *nla, struct nlmsgerr *nlerr, gpointer platform)
-{
- _LOGt ("event_err(): error from kernel: %s (%d) for request %d",
- strerror (nlerr ? -nlerr->error : 0),
- nlerr ? -nlerr->error : 0,
- NM_LINUX_PLATFORM_GET_PRIVATE (platform)->nlh_seq_last);
- return NL_OK;
+ if (seq_number != priv->nlh_seq_last_handled)
+ _LOGt ("netlink: recvmsg: unwaited sequence number %u", seq_number);
+ priv->nlh_seq_last_handled = seq_number;
}
-/* This function does all the magic to avoid race conditions caused
- * by concurrent usage of synchronous commands and an asynchronous cache. This
- * might be a nice future addition to libnl but it requires to do all operations
- * through the cache manager. In this case, nm-linux-platform serves as the
- * cache manager instead of the one provided by libnl.
- */
-static int
-event_notification (struct nl_msg *msg, gpointer user_data)
+static void
+event_valid_msg (NMPlatform *platform, struct nl_msg *msg)
{
- NMPlatform *platform = NM_PLATFORM (user_data);
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data);
+ NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
nm_auto_nmpobj NMPObject *obj = NULL;
nm_auto_nmpobj NMPObject *obj_cache = NULL;
NMPCacheOpsType cache_op;
@@ -3170,7 +3481,7 @@ event_notification (struct nl_msg *msg, gpointer user_data)
_LOGT ("event-notification: %s, seq %u: ignore",
_nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)),
msghdr->nlmsg_seq);
- return NL_OK;
+ return;
}
_LOGT ("event-notification: %s, seq %u: %s",
@@ -3199,190 +3510,6 @@ event_notification (struct nl_msg *msg, gpointer user_data)
}
cache_prune_candidates_drop (platform, obj_cache);
-
- return NL_OK;
-}
-
-/******************************************************************/
-
-static void
-_log_dbg_sysctl_set_impl (NMPlatform *platform, const char *path, const char *value)
-{
- GError *error = NULL;
- char *contents, *contents_escaped;
- char *value_escaped = g_strescape (value, NULL);
-
- if (!g_file_get_contents (path, &contents, NULL, &error)) {
- _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", path, value_escaped, error->message);
- g_clear_error (&error);
- } else {
- g_strstrip (contents);
- contents_escaped = g_strescape (contents, NULL);
- if (strcmp (contents, value) == 0)
- _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", path, value_escaped);
- else
- _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", path, value_escaped, contents_escaped);
- g_free (contents);
- g_free (contents_escaped);
- }
- g_free (value_escaped);
-}
-
-#define _log_dbg_sysctl_set(platform, path, value) \
- G_STMT_START { \
- if (_LOGD_ENABLED ()) { \
- _log_dbg_sysctl_set_impl (platform, path, value); \
- } \
- } G_STMT_END
-
-static gboolean
-sysctl_set (NMPlatform *platform, const char *path, const char *value)
-{
- int fd, len, nwrote, tries;
- char *actual;
-
- g_return_val_if_fail (path != NULL, FALSE);
- g_return_val_if_fail (value != NULL, FALSE);
-
- /* Don't write outside known locations */
- g_assert (g_str_has_prefix (path, "/proc/sys/")
- || g_str_has_prefix (path, "/sys/"));
- /* Don't write to suspicious locations */
- g_assert (!strstr (path, "/../"));
-
- fd = open (path, O_WRONLY | O_TRUNC);
- if (fd == -1) {
- if (errno == ENOENT) {
- _LOGD ("sysctl: failed to open '%s': (%d) %s",
- path, errno, strerror (errno));
- } else {
- _LOGE ("sysctl: failed to open '%s': (%d) %s",
- path, errno, strerror (errno));
- }
- return FALSE;
- }
-
- _log_dbg_sysctl_set (platform, path, value);
-
- /* Most sysfs and sysctl options don't care about a trailing LF, while some
- * (like infiniband) do. So always add the LF. Also, neither sysfs nor
- * sysctl support partial writes so the LF must be added to the string we're
- * about to write.
- */
- actual = g_strdup_printf ("%s\n", value);
-
- /* Try to write the entire value three times if a partial write occurs */
- len = strlen (actual);
- for (tries = 0, nwrote = 0; tries < 3 && nwrote != len; tries++) {
- nwrote = write (fd, actual, len);
- if (nwrote == -1) {
- if (errno == EINTR) {
- _LOGD ("sysctl: interrupted, will try again");
- continue;
- }
- break;
- }
- }
- if (nwrote == -1 && errno != EEXIST) {
- _LOGE ("sysctl: failed to set '%s' to '%s': (%d) %s",
- path, value, errno, strerror (errno));
- } else if (nwrote < len) {
- _LOGE ("sysctl: failed to set '%s' to '%s' after three attempts",
- path, value);
- }
-
- g_free (actual);
- close (fd);
- return (nwrote == len);
-}
-
-static GSList *sysctl_clear_cache_list;
-
-void
-_nm_linux_platform_sysctl_clear_cache (void)
-{
- while (sysctl_clear_cache_list) {
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (sysctl_clear_cache_list->data);
-
- sysctl_clear_cache_list = g_slist_delete_link (sysctl_clear_cache_list, sysctl_clear_cache_list);
-
- g_hash_table_destroy (priv->sysctl_get_prev_values);
- priv->sysctl_get_prev_values = NULL;
- priv->sysctl_get_warned = FALSE;
- }
-}
-
-static void
-_log_dbg_sysctl_get_impl (NMPlatform *platform, const char *path, const char *contents)
-{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- const char *prev_value = NULL;
-
- if (!priv->sysctl_get_prev_values) {
- sysctl_clear_cache_list = g_slist_prepend (sysctl_clear_cache_list, platform);
- priv->sysctl_get_prev_values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- } else
- prev_value = g_hash_table_lookup (priv->sysctl_get_prev_values, path);
-
- if (prev_value) {
- if (strcmp (prev_value, contents) != 0) {
- char *contents_escaped = g_strescape (contents, NULL);
- char *prev_value_escaped = g_strescape (prev_value, NULL);
-
- _LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", path, contents_escaped, prev_value_escaped);
- g_free (contents_escaped);
- g_free (prev_value_escaped);
- g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
- }
- } else {
- char *contents_escaped = g_strescape (contents, NULL);
-
- _LOGD ("sysctl: reading '%s': '%s'", path, contents_escaped);
- g_free (contents_escaped);
- g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (path), g_strdup (contents));
- }
-
- if ( !priv->sysctl_get_warned
- && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
- _LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
- priv->sysctl_get_warned = TRUE;
- }
-}
-
-#define _log_dbg_sysctl_get(platform, path, contents) \
- G_STMT_START { \
- if (_LOGD_ENABLED ()) \
- _log_dbg_sysctl_get_impl (platform, path, contents); \
- } G_STMT_END
-
-static char *
-sysctl_get (NMPlatform *platform, const char *path)
-{
- GError *error = NULL;
- char *contents;
-
- /* Don't write outside known locations */
- g_assert (g_str_has_prefix (path, "/proc/sys/")
- || g_str_has_prefix (path, "/sys/"));
- /* Don't write to suspicious locations */
- g_assert (!strstr (path, "/../"));
-
- if (!g_file_get_contents (path, &contents, NULL, &error)) {
- /* We assume FAILED means EOPNOTSUP */
- if ( g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)
- || g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_FAILED))
- _LOGD ("error reading %s: %s", path, error->message);
- else
- _LOGE ("error reading %s: %s", path, error->message);
- g_clear_error (&error);
- return NULL;
- }
-
- g_strstrip (contents);
-
- _log_dbg_sysctl_get (platform, path, contents);
-
- return contents;
}
/******************************************************************/
@@ -3489,71 +3616,65 @@ link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMP
/*****************************************************************************/
static gboolean
-do_add_link (NMPlatform *platform,
- NMLinkType link_type,
- const char *name,
- struct nl_msg *nlmsg)
+do_add_link_with_lookup (NMPlatform *platform,
+ NMLinkType link_type,
+ const char *name,
+ struct nl_msg *nlmsg,
+ const NMPlatformLink **out_link)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ const NMPObject *obj = NULL;
+ WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
int nle;
+ char s_buf[256];
- event_handler_read_netlink_all (platform, FALSE);
+ event_handler_read_netlink (platform, FALSE);
- nle = nl_send_auto (priv->nlh, nlmsg);
- if (nle < 0) {
- _LOGE ("do-add-link[%s/%s]: failure sending netlink request \"%s\" (%d)",
- name,
- nm_link_type_to_string (link_type),
- nl_geterror (nle), -nle);
- return FALSE;
+ if (nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) {
+ /* hm, a link with such a name already exists. Try reloading first. */
+ do_request_link (platform, 0, name);
+
+ obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL);
+ if (obj) {
+ _LOGE ("do-add-link[%s/%s]: link already exists: %s",
+ name,
+ nm_link_type_to_string (link_type),
+ nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0));
+ return FALSE;
+ }
}
- nle = nl_wait_for_ack (priv->nlh);
- switch (nle) {
- case -NLE_SUCCESS:
- _LOGD ("do-add-link[%s/%s]: success adding",
- name,
- nm_link_type_to_string (link_type));
- break;
- default:
- _LOGE ("do-add-link[%s/%s]: failed with \"%s\" (%d)",
+ nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
+ if (nle < 0) {
+ _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)",
name,
nm_link_type_to_string (link_type),
nl_geterror (nle), -nle);
return FALSE;
}
- delayed_action_handle_all (platform, TRUE);
+ delayed_action_handle_all (platform, FALSE);
- /* FIXME: we add the link object via the second netlink socket. Sometimes,
- * the notification is not yet ready via nlh_event, so we have to re-request the
- * link so that it is in the cache. A better solution would be to do everything
- * via one netlink socket. */
- if (!nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) {
- _LOGt ("do-add-link[%s/%s]: the added link is not yet ready. Request anew",
- name,
- nm_link_type_to_string (link_type));
- do_request_link (platform, 0, name, TRUE);
- }
+ nm_assert (seq_result);
- /* Return true, because the netlink request succeeded. This doesn't indicate that the
- * object is now actually in the cache, because there could be a race. */
- return TRUE;
-}
+ _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
+ ? LOGL_DEBUG
+ : LOGL_ERR,
+ "do-add-link[%s/%s]: %s",
+ name,
+ nm_link_type_to_string (link_type),
+ wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
-static gboolean
-do_add_link_with_lookup (NMPlatform *platform,
- NMLinkType link_type,
- const char *name,
- struct nl_msg *nlmsg,
- const NMPlatformLink **out_link)
-{
- const NMPObject *obj;
+ if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
+ obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL);
- do_add_link (platform, link_type, name, nlmsg);
+ if (!obj) {
+ /* either kernel signaled failure, or it signaled success and the link object
+ * is not (yet) in the cache. Try to reload it... */
+ do_request_link (platform, 0, name);
+ obj = nmp_cache_lookup_link_full (priv->cache, 0, name, FALSE, link_type, NULL, NULL);
+ }
- obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
- 0, name, FALSE, link_type, NULL, NULL);
if (out_link)
*out_link = obj ? &obj->link : NULL;
return !!obj;
@@ -3563,15 +3684,18 @@ static gboolean
do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
int nle;
+ char s_buf[256];
+ const NMPObject *obj;
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id),
NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS,
NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE));
- event_handler_read_netlink_all (platform, FALSE);
+ event_handler_read_netlink (platform, FALSE);
- nle = nl_send_auto (priv->nlh, nlmsg);
+ nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
if (nle < 0) {
_LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)",
NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
@@ -3580,137 +3704,108 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *
return FALSE;
}
- nle = nl_wait_for_ack (priv->nlh);
- switch (nle) {
- case -NLE_SUCCESS:
- _LOGD ("do-add-%s[%s]: success adding", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0));
- break;
- case -NLE_EXIST:
- /* NLE_EXIST is considered equivalent to success to avoid race conditions. You
- * never know when something sends an identical object just before
- * NetworkManager. */
- _LOGD ("do-add-%s[%s]: adding link failed with \"%s\" (%d), meaning such a link already exists",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- break;
- default:
- _LOGE ("do-add-%s[%s]: failed with \"%s\" (%d)",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- return FALSE;
- }
+ delayed_action_handle_all (platform, FALSE);
- delayed_action_handle_all (platform, TRUE);
+ nm_assert (seq_result);
- /* FIXME: instead of re-requesting the added object, add it via nlh_event
- * so that the events are in sync. */
- if (!nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_id)) {
- _LOGt ("do-add-%s[%s]: the added object is not yet ready. Request anew",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0));
- do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE);
+ _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
+ ? LOGL_DEBUG
+ : LOGL_ERR,
+ "do-add-%s[%s]: %s",
+ NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
+ nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
+ wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
+
+ /* In rare cases, the object is not yet ready as we received the ACK from
+ * kernel. Need to refetch.
+ *
+ * We want to safe the expensive refetch, thus we look first into the cache
+ * whether the object exists.
+ *
+ * FIXME: if the object already existed previously, we might not notice a
+ * missing update. It's not clear how to fix that reliably without refechting
+ * all the time. */
+ obj = nmp_cache_lookup_obj (priv->cache, obj_id);
+ if (!obj) {
+ do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id));
+ obj = nmp_cache_lookup_obj (priv->cache, obj_id);
}
- /* The return value doesn't say, whether the object is in the platform cache after adding
- * it. Instead the return value says, whether the netlink request succeeded. */
- return TRUE;
+ /* Adding is only successful, if kernel reported success *and* we have the
+ * expected object in cache afterwards. */
+ return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
}
static gboolean
do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
int nle;
+ char s_buf[256];
+ gboolean success = TRUE;
+ const char *log_detail = "";
- event_handler_read_netlink_all (platform, FALSE);
+ event_handler_read_netlink (platform, FALSE);
- nle = nl_send_auto (priv->nlh, nlmsg);
+ nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
if (nle < 0) {
_LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)",
NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
nl_geterror (nle), -nle);
- return FALSE;
+ goto out;
}
- nle = nl_wait_for_ack (priv->nlh);
- switch (nle) {
- case -NLE_SUCCESS:
- _LOGD ("do-delete-%s[%s]: success deleting", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0));
- break;
- case -NLE_OBJ_NOTFOUND:
- _LOGD ("do-delete-%s[%s]: failed with \"%s\" (%d), meaning the object was already removed",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- break;
- case -NLE_FAILURE:
- if (NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP6_ADDRESS)
- goto nle_failure;
-
- /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */
- _LOGD ("do-delete-%s[%s]: deleting address failed with \"%s\" (%d), meaning the address was already removed",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- break;
- case -NLE_NOADDR:
- if ( NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP4_ADDRESS
- && NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP6_ADDRESS)
- goto nle_failure;
-
- _LOGD ("do-delete-%s[%s]: deleting address failed with \"%s\" (%d), meaning the address was already removed",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- break;
- default:
-nle_failure:
- _LOGE ("do-delete-%s[%s]: failed with \"%s\" (%d)",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
- nl_geterror (nle), -nle);
- return FALSE;
- }
+ delayed_action_handle_all (platform, FALSE);
- delayed_action_handle_all (platform, TRUE);
+ nm_assert (seq_result);
+
+ if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) {
+ /* ok */
+ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT))
+ log_detail = ", meaning the object was already removed";
+ else if ( NM_IN_SET (-((int) seq_result), ENXIO)
+ && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP6_ADDRESS)) {
+ /* On RHEL7 kernel, deleting a non existing address fails with ENXIO */
+ log_detail = ", meaning the address was already removed";
+ } else if ( NM_IN_SET (-((int) seq_result), EADDRNOTAVAIL)
+ && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS))
+ log_detail = ", meaning the address was already removed";
+ else
+ success = FALSE;
- /* FIXME: instead of re-requesting the deleted object, add it via nlh_event
- * so that the events are in sync. */
- if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_LINK) {
- const NMPObject *obj;
+ _NMLOG (success ? LOGL_DEBUG : LOGL_ERR,
+ "do-delete-%s[%s]: %s%s",
+ NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
+ nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0),
+ wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)),
+ log_detail);
- obj = nmp_cache_lookup_link_full (priv->cache, obj_id->link.ifindex, obj_id->link.ifindex <= 0 && obj_id->link.name[0] ? obj_id->link.name : NULL, FALSE, NM_LINK_TYPE_NONE, NULL, NULL);
- if (obj && obj->_link.netlink.is_in_netlink) {
- _LOGt ("do-delete-%s[%s]: reload: the deleted object is not yet removed. Request anew",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0));
- do_request_link (platform, obj_id->link.ifindex, obj_id->link.name, TRUE);
- }
- } else {
- if (nmp_cache_lookup_obj (priv->cache, obj_id)) {
- _LOGt ("do-delete-%s[%s]: reload: the deleted object is not yet removed. Request anew",
- NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name,
- nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0));
- do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE);
- }
- }
+out:
+ if (!nmp_cache_lookup_obj (priv->cache, obj_id))
+ return TRUE;
- /* The return value doesn't say, whether the object is in the platform cache after adding
- * it. Instead the return value says, whether the netlink request succeeded. */
- return TRUE;
+ /* such an object still exists in the cache. To be sure, refetch it (and
+ * hope it's gone) */
+ do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id));
+ return !!nmp_cache_lookup_obj (priv->cache, obj_id);
}
static NMPlatformError
-do_change_link (NMPlatform *platform, int ifindex, struct nl_msg *nlmsg)
+do_change_link (NMPlatform *platform,
+ int ifindex,
+ struct nl_msg *nlmsg)
{
- NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
+ WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
int nle;
+ char s_buf[256];
+ NMPlatformError result = NM_PLATFORM_ERROR_SUCCESS;
+ NMLogLevel log_level = LOGL_DEBUG;
+ const char *log_result = "failure", *log_detail = "";
retry:
- nle = nl_send_auto_complete (priv->nlh, nlmsg);
+ nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result);
if (nle < 0) {
_LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)",
ifindex,
@@ -3718,35 +3813,39 @@ retry:
return NM_PLATFORM_ERROR_UNSPECIFIED;
}
- nle = nl_wait_for_ack (priv->nlh);
- if ( nle == -NLE_OPNOTSUPP
+ /* always refetch the link after changing it. There seems to be issues
+ * and we sometimes lack events. Nuke it from the orbit... */
+ delayed_action_schedule (platform, DELAYED_ACTION_TYPE_REFRESH_LINK, GINT_TO_POINTER (ifindex));
+
+ delayed_action_handle_all (platform, FALSE);
+
+ nm_assert (seq_result);
+
+ if ( NM_IN_SET (-((int) seq_result), EOPNOTSUPP)
&& nlmsg_hdr (nlmsg)->nlmsg_type == RTM_NEWLINK) {
nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK;
goto retry;
}
- switch (nle) {
- case -NLE_SUCCESS:
- _LOGD ("do-change-link[%d]: success changing link", ifindex);
- break;
- case -NLE_EXIST:
- _LOGD ("do-change-link[%d]: success changing link: %s (%d)",
- ifindex, nl_geterror (nle), -nle);
- break;
- case -NLE_OBJ_NOTFOUND:
- _LOGD ("do-change-link[%d]: failure changing link: firmware not found (%s, %d)",
- ifindex, nl_geterror (nle), -nle);
- return NM_PLATFORM_ERROR_NO_FIRMWARE;
- default:
- _LOGE ("do-change-link[%d]: failure changing link: netlink error (%s, %d)",
- ifindex, nl_geterror (nle), -nle);
- return NM_PLATFORM_ERROR_UNSPECIFIED;
+ if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) {
+ log_result = "success";
+ } else if (NM_IN_SET (-((int) seq_result), EEXIST, EADDRINUSE)) {
+ /* */
+ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) {
+ log_detail = ", firmware not found";
+ result = NM_PLATFORM_ERROR_NO_FIRMWARE;
+ } else {
+ log_level = LOGL_ERR;
+ result = NM_PLATFORM_ERROR_UNSPECIFIED;
}
+ _NMLOG (log_level,
+ "do-change-link[%d]: %s changing link: %s%s",
+ ifindex,
+ log_result,
+ wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)),
+ log_detail);
- /* FIXME: as we modify the link via a separate socket, the cache is not in
- * sync and we have to refetch the link. */
- do_request_link (platform, ifindex, NULL, TRUE);
- return NM_PLATFORM_ERROR_SUCCESS;
+ return result;
}
static gboolean
@@ -3860,7 +3959,7 @@ link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged)
static gboolean
link_refresh (NMPlatform *platform, int ifindex)
{
- do_request_link (platform, ifindex, NULL, TRUE);
+ do_request_link (platform, ifindex, NULL);
return !!cache_lookup_link (platform, ifindex);
}
@@ -3970,7 +4069,7 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable
if ( !nlmsg
|| !_nl_msg_new_link_set_afspec (nlmsg,
mode))
- return FALSE;
+ g_return_val_if_reached (FALSE);
return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
}
@@ -4490,7 +4589,6 @@ link_vxlan_add (NMPlatform *platform,
nla_nest_end (nlmsg, info);
return do_add_link_with_lookup (platform, NM_LINK_TYPE_VXLAN, name, nlmsg, out_link);
-
nla_put_failure:
g_return_val_if_reached (FALSE);
}
@@ -4660,7 +4758,7 @@ link_vlan_change (NMPlatform *platform,
new_n_ingress_map,
new_egress_map,
new_n_egress_map))
- return FALSE;
+ g_return_val_if_reached (FALSE);
return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS;
}
@@ -4714,7 +4812,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap,
close (fd);
return FALSE;
}
- do_request_link (platform, 0, name, TRUE);
+ do_request_link (platform, 0, name);
obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
0, name, FALSE,
tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN,
@@ -4778,7 +4876,7 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, const NMP
if (!nm_platform_sysctl_set (platform, path, id))
return FALSE;
- do_request_link (platform, 0, ifname, TRUE);
+ do_request_link (platform, 0, ifname);
obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
0, ifname, FALSE, NM_LINK_TYPE_INFINIBAND, NULL, NULL);
@@ -5097,6 +5195,8 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen,
NM_PLATFORM_LIFETIME_PERMANENT,
NM_PLATFORM_LIFETIME_PERMANENT,
NULL);
+ if (!nlmsg)
+ g_return_val_if_reached (FALSE);
nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address);
return do_delete_object (platform, &obj_id, nlmsg);
@@ -5120,6 +5220,8 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int
NM_PLATFORM_LIFETIME_PERMANENT,
NM_PLATFORM_LIFETIME_PERMANENT,
NULL);
+ if (!nlmsg)
+ g_return_val_if_reached (FALSE);
nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen);
return do_delete_object (platform, &obj_id, nlmsg);
@@ -5285,7 +5387,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
* FIXME: once our ip4_address_add() is sure that upon return we have
* the latest state from in the platform cache, we might save this
* additional expensive cache-resync. */
- do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE, TRUE);
+ do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE);
if (!nmp_cache_lookup_obj (priv->cache, &obj_id))
return TRUE;
@@ -5372,22 +5474,6 @@ ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int p
#define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
#define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
-static int
-verify_source (struct nl_msg *msg, NMPlatform *platform)
-{
- struct ucred *creds = nlmsg_get_creds (msg);
-
- if (!creds || creds->pid) {
- if (creds)
- _LOGW ("netlink: received non-kernel message (pid %d)", creds->pid);
- else
- _LOGW ("netlink: received message without credentials");
- return NL_STOP;
- }
-
- return NL_OK;
-}
-
static gboolean
event_handler (GIOChannel *channel,
GIOCondition io_condition,
@@ -5397,114 +5483,288 @@ event_handler (GIOChannel *channel,
return TRUE;
}
-static gboolean
-event_handler_read_netlink_one (NMPlatform *platform)
+/*****************************************************************************/
+
+/* copied from libnl3's recvmsgs() */
+static int
+event_handler_recvmsgs (NMPlatform *platform, gboolean handle_events)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- int nle;
-
+ struct nl_sock *sk = priv->nlh;
+ int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0;
+ unsigned char *buf = NULL;
+ struct nlmsghdr *hdr;
+ WaitForNlResponseResult seq_result;
+
+ /*
+ nla is passed on to not only to nl_recv() but may also be passed
+ to a function pointer provided by the caller which may or may not
+ initialize the variable. Thomas Graf.
+ */
+ struct sockaddr_nl nla = {0};
+ struct nl_msg *msg = NULL;
+ struct ucred *creds = NULL;
+
+continue_reading:
errno = 0;
- nle = nl_recvmsgs_default (priv->nlh_event);
+ n = nl_recv (sk, &nla, &buf, &creds);
/* Work around a libnl bug fixed in 3.2.22 (375a6294) */
- if (nle == 0 && errno == EAGAIN) {
+ if (n == 0 && errno == EAGAIN) {
/* EAGAIN is equal to EWOULDBLOCK. If it would not be, we'd have to
* workaround libnl3 mapping EWOULDBLOCK to -NLE_FAILURE. */
G_STATIC_ASSERT (EAGAIN == EWOULDBLOCK);
- nle = -NLE_AGAIN;
+ n = -NLE_AGAIN;
}
- if (nle < 0)
- switch (nle) {
- case -NLE_AGAIN:
- return FALSE;
- case -NLE_DUMP_INTR:
- _LOGD ("Uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
- break;
- case -NLE_NOMEM:
- _LOGI ("Too many netlink events. Need to resynchronize platform cache");
- /* Drain the event queue, we've lost events and are out of sync anyway and we'd
- * like to free up some space. We'll read in the status synchronously. */
- _nl_sock_flush_data (priv->nlh_event);
- priv->nlh_seq_expect = 0;
- delayed_action_schedule (platform,
- DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS |
- DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
- DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
- DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
- DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
- NULL);
- break;
- default:
- _LOGE ("Failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
- break;
+ if (n <= 0)
+ return n;
+
+ if (!handle_events) {
+ /* we read until failure or there is nothing to read (EAGAIN). */
+ goto continue_reading;
}
- return TRUE;
+
+ hdr = (struct nlmsghdr *) buf;
+ while (nlmsg_ok (hdr, n)) {
+ gboolean abort_parsing = FALSE;
+
+ nlmsg_free (msg);
+ msg = nlmsg_convert (hdr);
+ if (!msg) {
+ err = -NLE_NOMEM;
+ goto out;
+ }
+
+ nlmsg_set_proto (msg, NETLINK_ROUTE);
+ nlmsg_set_src (msg, &nla);
+ nrecv++;
+
+ if (!creds || creds->pid) {
+ if (creds)
+ _LOGD ("netlink: recvmsg: received non-kernel message (pid %d)", creds->pid);
+ else
+ _LOGD ("netlink: recvmsg: received message without credentials");
+ goto stop;
+ }
+
+ _LOGt ("netlink: recvmsg: new message type %d, seq %u",
+ hdr->nlmsg_type, hdr->nlmsg_seq);
+
+ if (creds)
+ nlmsg_set_creds (msg, creds);
+
+ if (hdr->nlmsg_flags & NLM_F_MULTI)
+ multipart = 1;
+
+ if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
+ /*
+ * We have to continue reading to clear
+ * all messages until a NLMSG_DONE is
+ * received and report the inconsistency.
+ */
+ interrupted = 1;
+ }
+
+ /* Other side wishes to see an ack for this message */
+ if (hdr->nlmsg_flags & NLM_F_ACK) {
+ /* FIXME: implement */
+ }
+
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN;
+
+ if (hdr->nlmsg_type == NLMSG_DONE) {
+ /* messages terminates a multipart message, this is
+ * usually the end of a message and therefore we slip
+ * out of the loop by default. the user may overrule
+ * this action by skipping this packet. */
+ multipart = 0;
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
+ } else if (hdr->nlmsg_type == NLMSG_NOOP) {
+ /* Message to be ignored, the default action is to
+ * skip this message if no callback is specified. The
+ * user may overrule this action by returning
+ * NL_PROCEED. */
+ } else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
+ /* Data got lost, report back to user. The default action is to
+ * quit parsing. The user may overrule this action by retuning
+ * NL_SKIP or NL_PROCEED (dangerous) */
+ err = -NLE_MSG_OVERFLOW;
+ abort_parsing = TRUE;
+ } else if (hdr->nlmsg_type == NLMSG_ERROR) {
+ /* Message carries a nlmsgerr */
+ struct nlmsgerr *e = nlmsg_data (hdr);
+
+ if (hdr->nlmsg_len < nlmsg_size (sizeof (*e))) {
+ /* Truncated error message, the default action
+ * is to stop parsing. The user may overrule
+ * this action by returning NL_SKIP or
+ * NL_PROCEED (dangerous) */
+ err = -NLE_MSG_TRUNC;
+ abort_parsing = TRUE;
+ } else if (e->error) {
+ int errsv = e->error > 0 ? e->error : -e->error;
+
+ /* Error message reported back from kernel. */
+ _LOGD ("netlink: recvmsg: error message from kernel: %s (%d) for request %d",
+ strerror (errsv),
+ errsv,
+ nlmsg_hdr (msg)->nlmsg_seq);
+ seq_result = -errsv;
+ } else
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
+ } else {
+ /* Valid message (not checking for MULTIPART bit to
+ * get along with broken kernels. NL_SKIP has no
+ * effect on this. */
+ event_valid_msg (platform, msg);
+ seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
+ }
+
+ event_seq_check (platform, msg, seq_result);
+ err = 0;
+ hdr = nlmsg_next (hdr, &n);
+
+ if (abort_parsing)
+ goto out;
+ }
+
+ nlmsg_free (msg);
+ free (buf);
+ free (creds);
+ buf = NULL;
+ msg = NULL;
+ creds = NULL;
+
+ if (multipart) {
+ /* Multipart message not yet complete, continue reading */
+ goto continue_reading;
+ }
+stop:
+ err = 0;
+out:
+ nlmsg_free (msg);
+ free (buf);
+ free (creds);
+
+ if (interrupted)
+ err = -NLE_DUMP_INTR;
+
+ if (!err)
+ err = nrecv;
+
+ return err;
}
+/*****************************************************************************/
+
static gboolean
-event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks)
+event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
- int r;
+ int r, nle;
struct pollfd pfd;
gboolean any = FALSE;
- gint64 timestamp = 0, now;
- const int TIMEOUT = 250;
- int timeout = 0;
- guint32 wait_for_seq = 0;
+ gint64 now_ns;
+ int timeout_ms;
+ guint i;
+ struct {
+ guint32 seq_number;
+ gint64 timeout_abs_ns;
+ } data_next;
while (TRUE) {
- while (event_handler_read_netlink_one (platform))
+
+ while (TRUE) {
+
+ nle = event_handler_recvmsgs (platform, TRUE);
+
+ if (nle < 0)
+ switch (nle) {
+ case -NLE_AGAIN:
+ goto after_read;
+ case -NLE_DUMP_INTR:
+ _LOGD ("netlink: read: uncritical failure to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
+ break;
+ case -NLE_NOMEM:
+ _LOGI ("netlink: read: too many netlink events. Need to resynchronize platform cache");
+ /* Drain the event queue, we've lost events and are out of sync anyway and we'd
+ * like to free up some space. We'll read in the status synchronously. */
+ delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC);
+ event_handler_recvmsgs (platform, FALSE);
+ delayed_action_schedule (platform,
+ DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
+ DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+ NULL);
+ break;
+ default:
+ _LOGE ("netlink: read: failed to retrieve incoming events: %s (%d)", nl_geterror (nle), nle);
+ break;
+ }
any = TRUE;
+ }
+
+after_read:
- if (!wait_for_acks || priv->nlh_seq_expect == 0) {
- if (wait_for_seq)
- _LOGt ("read-netlink-all: ACK for sequence number %u received", priv->nlh_seq_expect);
+ if (!NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
return any;
- }
- now = nm_utils_get_monotonic_timestamp_ms ();
- if (wait_for_seq != priv->nlh_seq_expect) {
- /* We are waiting for a new sequence number (or we will wait for the first time).
- * Reset/start counting the overall wait time. */
- _LOGt ("read-netlink-all: wait for ACK for sequence number %u...", priv->nlh_seq_expect);
- wait_for_seq = priv->nlh_seq_expect;
- timestamp = now;
- timeout = TIMEOUT;
- } else {
- if ((now - timestamp) >= TIMEOUT) {
- /* timeout. Don't wait for this sequence number anymore. */
- break;
- }
+ now_ns = 0;
+ data_next.seq_number = 0;
+ data_next.timeout_abs_ns = 0;
+
+ for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; ) {
+ DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i);
+
+ if (data->seq_result)
+ delayed_action_wait_for_nl_response_complete (platform, i, data->seq_result);
+ else if ((now_ns ?: (now_ns = nm_utils_get_monotonic_timestamp_ns ())) > data->timeout_abs_ns)
+ delayed_action_wait_for_nl_response_complete (platform, i, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_TIMEOUT);
+ else {
+ i++;
- /* readjust the wait-time. */
- timeout = TIMEOUT - (now - timestamp);
+ if ( data_next.seq_number == 0
+ || data_next.timeout_abs_ns > data->timeout_abs_ns)
+ data_next.seq_number = data->seq_number;
+ data_next.timeout_abs_ns = data->timeout_abs_ns;
+ }
}
+ if ( !wait_for_acks
+ || !NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE))
+ return any;
+
+ nm_assert (data_next.seq_number);
+ nm_assert (data_next.timeout_abs_ns > 0);
+ nm_assert (now_ns > 0);
+
+ _LOGT ("netlink: read: wait for ACK for sequence number %u...", data_next.seq_number);
+
+ timeout_ms = (data_next.timeout_abs_ns - now_ns) / (NM_UTILS_NS_PER_SECOND / 1000);
+
memset (&pfd, 0, sizeof (pfd));
- pfd.fd = nl_socket_get_fd (priv->nlh_event);
+ pfd.fd = nl_socket_get_fd (priv->nlh);
pfd.events = POLLIN;
- r = poll (&pfd, 1, timeout);
+ r = poll (&pfd, 1, MAX (1, timeout_ms));
if (r == 0) {
- /* timeout. */
- break;
+ /* timeout and there is nothing to read. */
+ goto after_read;
}
if (r < 0) {
int errsv = errno;
if (errsv != EINTR) {
- _LOGE ("read-netlink-all: poll failed with %s", strerror (errsv));
+ _LOGE ("netlink: read: poll failed with %s", strerror (errsv));
+ delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_POLL);
return any;
}
/* Continue to read again, even if there might be nothing to read after EINTR. */
}
}
-
- _LOGW ("read-netlink-all: timeout waiting for ACK to sequence number %u...", wait_for_seq);
- priv->nlh_seq_expect = 0;
- return any;
}
/******************************************************************/
@@ -5621,9 +5881,11 @@ nm_linux_platform_init (NMLinuxPlatform *self)
self->priv = priv;
+ priv->nlh_seq_next = 1;
priv->cache = nmp_cache_new ();
priv->delayed_action.list_master_connected = g_ptr_array_new ();
priv->delayed_action.list_refresh_link = g_ptr_array_new ();
+ priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
}
@@ -5645,36 +5907,13 @@ constructed (GObject *_object)
priv->nlh = nl_socket_alloc ();
g_assert (priv->nlh);
- nle = nl_socket_modify_cb (priv->nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, (nl_recvmsg_msg_cb_t) verify_source, platform);
- g_assert (!nle);
-
nle = nl_connect (priv->nlh, NETLINK_ROUTE);
g_assert (!nle);
nle = nl_socket_set_passcred (priv->nlh, 1);
g_assert (!nle);
- }
- _LOGD ("Netlink socket for requests established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh));
-
- {
- priv->nlh_event = nl_socket_alloc ();
- g_assert (priv->nlh_event);
-
- nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_MSG_IN, NL_CB_CUSTOM, (nl_recvmsg_msg_cb_t) verify_source, platform);
- g_assert (!nle);
- nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_VALID, NL_CB_CUSTOM, event_notification, platform);
- g_assert (!nle);
- nle = nl_socket_modify_cb (priv->nlh_event, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, event_seq_check, platform);
- g_assert (!nle);
- nle = nl_socket_modify_err_cb (priv->nlh_event, NL_CB_CUSTOM, event_err, platform);
- g_assert (!nle);
-
- nle = nl_connect (priv->nlh_event, NETLINK_ROUTE);
- g_assert (!nle);
- nle = nl_socket_set_passcred (priv->nlh_event, 1);
- g_assert (!nle);
/* No blocking for event socket, so that we can drain it safely. */
- nle = nl_socket_set_nonblocking (priv->nlh_event);
+ nle = nl_socket_set_nonblocking (priv->nlh);
g_assert (!nle);
/* The default buffer size wasn't enough for the testsuites. It might just
@@ -5684,19 +5923,19 @@ constructed (GObject *_object)
* FIXME: it's unclear that this is still actually needed. The testsuite
* certainly doesn't fail for me. Maybe it can be removed.
*/
- nle = nl_socket_set_buffer_size (priv->nlh_event, 131072, 0);
+ nle = nl_socket_set_buffer_size (priv->nlh, 131072, 0);
g_assert (!nle);
- nle = nl_socket_add_memberships (priv->nlh_event,
+ nle = nl_socket_add_memberships (priv->nlh,
RTNLGRP_LINK,
RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR,
RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE,
0);
g_assert (!nle);
}
- _LOGD ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh_event), nl_socket_get_fd (priv->nlh_event));
+ _LOGD ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh));
- priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh_event));
+ priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh));
g_io_channel_set_encoding (priv->event_channel, NULL, NULL);
g_io_channel_set_close_on_unref (priv->event_channel, TRUE);
@@ -5749,12 +5988,12 @@ dispose (GObject *object)
_LOGD ("dispose");
+ delayed_action_wait_for_nl_response_complete_all (platform, WAIT_FOR_NL_RESPONSE_RESULT_FAILED_DISPOSING);
+
priv->delayed_action.flags = DELAYED_ACTION_TYPE_NONE;
g_ptr_array_set_size (priv->delayed_action.list_master_connected, 0);
g_ptr_array_set_size (priv->delayed_action.list_refresh_link, 0);
- nm_clear_g_source (&priv->delayed_action.idle_id);
-
g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
@@ -5769,12 +6008,12 @@ nm_linux_platform_finalize (GObject *object)
g_ptr_array_unref (priv->delayed_action.list_master_connected);
g_ptr_array_unref (priv->delayed_action.list_refresh_link);
+ g_array_unref (priv->delayed_action.list_wait_for_nl_response);
/* Free netlink resources */
g_source_remove (priv->event_id);
g_io_channel_unref (priv->event_channel);
nl_socket_free (priv->nlh);
- nl_socket_free (priv->nlh_event);
g_object_unref (priv->udev_client);
g_hash_table_unref (priv->wifi_data);
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 2b1ec2127f..6fbbbe2357 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -1072,6 +1072,24 @@ nmp_cache_id_init_routes_visible (NMPCacheId *id,
return id;
}
+NMPCacheId *
+nmp_cache_id_init_link_by_ifname (NMPCacheId *id,
+ const char *ifname)
+{
+ gsize l;
+
+ _nmp_cache_id_init (id, NMP_CACHE_ID_TYPE_LINK_BY_IFNAME);
+
+ if ( !ifname
+ || (l = strlen (ifname)) > sizeof (id->link_by_ifname.ifname_short))
+ g_return_val_if_reached (id);
+
+ /* the trailing NUL is dropped!! */
+ memcpy (id->link_by_ifname.ifname_short, ifname, l);
+
+ return id;
+}
+
/******************************************************************/
static gboolean
@@ -1096,6 +1114,23 @@ _nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCach
}
static gboolean
+_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
+{
+ switch (id_type) {
+ case NMP_CACHE_ID_TYPE_LINK_BY_IFNAME:
+ if (obj->link.name[0]) {
+ *out_id = nmp_cache_id_init_link_by_ifname (id, obj->link.name);
+ return TRUE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ *out_id = NULL;
+ return TRUE;
+}
+
+static gboolean
_vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id)
{
switch (id_type) {
@@ -1356,7 +1391,7 @@ nmp_cache_lookup_link_full (const NMPCache *cache,
const NMPObject *obj;
const NMPlatformObject *const *list;
guint i, len;
- NMPCacheId cache_id;
+ NMPCacheId cache_id, *p_cache_id;
if (ifindex > 0) {
obj = nmp_cache_lookup_obj (cache, nmp_object_stackinit_id_link (&obj_needle, ifindex));
@@ -1371,7 +1406,14 @@ nmp_cache_lookup_link_full (const NMPCache *cache,
} else if (!ifname && !match_fn)
return NULL;
else {
- list = nmp_cache_lookup_multi (cache, nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only), &len);
+ if ( ifname
+ && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) {
+ p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname);
+ ifname = NULL;
+ } else
+ p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only);
+
+ list = nmp_cache_lookup_multi (cache, p_cache_id, &len);
for (i = 0; i < len; i++) {
obj = NMP_OBJECT_UP_CAST (list[i]);
@@ -1933,6 +1975,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
.rtm_gettype = RTM_GETLINK,
.signal_type_id = NM_PLATFORM_SIGNAL_ID_LINK,
.signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED,
+ .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link,
.cmd_obj_cmp = _vt_cmd_obj_cmp_link,
.cmd_obj_copy = _vt_cmd_obj_copy_link,
.cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link,
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 5acde46075..ab1cc2fe8e 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -64,6 +64,9 @@ typedef enum { /*< skip >*/
/* all the objects of a certain type */
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
+ /* index for the link objects by ifname. */
+ NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
+
/* all the visible objects of a certain type */
NMP_CACHE_ID_TYPE_OBJECT_TYPE_VISIBLE_ONLY,
@@ -104,6 +107,11 @@ typedef struct {
guint8 obj_type; /* NMPObjectType as guint8 */
int ifindex;
} object_type_by_ifindex;
+ struct {
+ /* NMP_CACHE_ID_TYPE_LINK_BY_IFNAME */
+ guint8 _id_type;
+ char ifname_short[IFNAMSIZ - 1]; /* don't include the trailing NUL so the struct fits in 4 bytes. */
+ } link_by_ifname;
};
} NMPCacheId;
@@ -374,6 +382,7 @@ void nmp_cache_id_destroy (NMPCacheId *id);
NMPCacheId *nmp_cache_id_init_object_type (NMPCacheId *id, NMPObjectType obj_type, gboolean visible_only);
NMPCacheId *nmp_cache_id_init_addrroute_visible_by_ifindex (NMPCacheId *id, NMPObjectType obj_type, int ifindex);
NMPCacheId *nmp_cache_id_init_routes_visible (NMPCacheId *id, NMPObjectType obj_type, gboolean with_default, gboolean with_non_default, int ifindex);
+NMPCacheId *nmp_cache_id_init_link_by_ifname (NMPCacheId *id, const char *ifname);
const NMPlatformObject *const *nmp_cache_lookup_multi (const NMPCache *cache, const NMPCacheId *cache_id, guint *out_len);
GArray *nmp_cache_lookup_multi_to_array (const NMPCache *cache, NMPObjectType obj_type, const NMPCacheId *cache_id);
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index af6c4878b2..8b4962620b 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -32,16 +32,16 @@ test_bogus(void)
g_assert (!nm_platform_link_get_type (NM_PLATFORM_GET, BOGUS_IFINDEX));
g_assert (!nm_platform_link_get_type_name (NM_PLATFORM_GET, BOGUS_IFINDEX));
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: netlink error (No such device*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
g_assert (!nm_platform_link_set_up (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: netlink error (No such device*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
g_assert (!nm_platform_link_set_down (NM_PLATFORM_GET, BOGUS_IFINDEX));
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: netlink error (No such device*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
g_assert (!nm_platform_link_set_arp (NM_PLATFORM_GET, BOGUS_IFINDEX));
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: netlink error (No such device*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
g_assert (!nm_platform_link_set_noarp (NM_PLATFORM_GET, BOGUS_IFINDEX));
g_assert (!nm_platform_link_is_up (NM_PLATFORM_GET, BOGUS_IFINDEX));
@@ -52,7 +52,7 @@ test_bogus(void)
g_assert (!addrlen);
g_assert (!nm_platform_link_get_address (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL));
- g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: netlink error (No such device*");
+ g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING, "*failure changing link: *");
g_assert (!nm_platform_link_set_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX, MTU));
g_assert (!nm_platform_link_get_mtu (NM_PLATFORM_GET, BOGUS_IFINDEX));