summary refs log tree commit diff
path: root/libnm-core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-02-07 02:39:23 +0100
committerMichael Biebl <biebl@debian.org>2018-02-07 02:39:23 +0100
commit50f6b47074e01dffb8dc536c0a20961dcf28ae9b (patch)
treeb4bd771d78ebe38661b8c4ec8b9a8e0eb8261eb5 /libnm-core
parentafcd268ea7b1149fbfb66bce4eca659b675da0a2 (diff)
New upstream version 1.10.4 upstream/1.10.4
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/nm-core-internal.h8
-rw-r--r--libnm-core/nm-dbus-interface.h4
-rw-r--r--libnm-core/nm-dbus-types.xml10
-rw-r--r--libnm-core/nm-json.c118
-rw-r--r--libnm-core/nm-json.h50
-rw-r--r--libnm-core/nm-setting-ip4-config.c2
-rw-r--r--libnm-core/nm-setting-tc-config.h3
-rw-r--r--libnm-core/nm-setting-team.c10
-rw-r--r--libnm-core/nm-setting-team.h2
-rw-r--r--libnm-core/nm-setting-vpn.c23
-rw-r--r--libnm-core/nm-utils.c177
-rw-r--r--libnm-core/tests/test-general.c99
12 files changed, 433 insertions, 73 deletions
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 5a27d438..15e5dc2a 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -241,7 +241,13 @@ GPtrArray *_nm_utils_copy_object_array (const GPtrArray *array);
 
 gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer needle);
 
-gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
+gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+                                              gsize len,
+                                              gconstpointer needle,
+                                              GCompareDataFunc cmpfcn,
+                                              gpointer user_data,
+                                              gssize *out_idx_first,
+                                              gssize *out_idx_last);
 gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
 
 GSList *    _nm_utils_strv_to_slist (char **strv, gboolean deep_copy);
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 98c94154..33e4850c 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -551,6 +551,8 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_PARENT_CHANGED: the device's parent changed
  * @NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED: the device parent's management changed
  * @NM_DEVICE_STATE_REASON_OVSDB_FAILED: problem communicating with OpenVSwitch database
+ * @NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE: a duplicate IP address was detected
+ * @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported
  *
  * Device state change reason codes
  */
@@ -619,6 +621,8 @@ typedef enum {
 	NM_DEVICE_STATE_REASON_PARENT_CHANGED                 = 61,
 	NM_DEVICE_STATE_REASON_PARENT_MANAGED_CHANGED         = 62,
 	NM_DEVICE_STATE_REASON_OVSDB_FAILED                   = 63,
+	NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE           = 64,
+	NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED          = 65,
 } NMDeviceStateReason;
 
 /**
diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml
index bc0c023b..b8c5d84f 100644
--- a/libnm-core/nm-dbus-types.xml
+++ b/libnm-core/nm-dbus-types.xml
@@ -1122,6 +1122,16 @@
               <entry role="enum_member_value"><para>= <literal>63</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>problem communicating with OpenVSwitch database</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>64</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>a duplicate IP address was detected</para><para></para></entry>
+            </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>65</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>The selected IP method is not supported</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c
new file mode 100644
index 00000000..f9042b1f
--- /dev/null
+++ b/libnm-core/nm-json.c
@@ -0,0 +1,118 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017, 2018 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#define NM_JANSSON_C
+#include "nm-json.h"
+
+#include <dlfcn.h>
+
+void *_nm_jansson_json_object_iter_value;
+void *_nm_jansson_json_object_key_to_iter;
+void *_nm_jansson_json_integer;
+void *_nm_jansson_json_object_del;
+void *_nm_jansson_json_array_get;
+void *_nm_jansson_json_array_size;
+void *_nm_jansson_json_array_append_new;
+void *_nm_jansson_json_string;
+void *_nm_jansson_json_object_iter_next;
+void *_nm_jansson_json_loads;
+void *_nm_jansson_json_dumps;
+void *_nm_jansson_json_object_iter_key;
+void *_nm_jansson_json_object;
+void *_nm_jansson_json_object_get;
+void *_nm_jansson_json_array;
+void *_nm_jansson_json_false;
+void *_nm_jansson_json_delete;
+void *_nm_jansson_json_true;
+void *_nm_jansson_json_object_size;
+void *_nm_jansson_json_object_set_new;
+void *_nm_jansson_json_object_iter;
+void *_nm_jansson_json_object_iter_at;
+void *_nm_jansson_json_integer_value;
+void *_nm_jansson_json_string_value;
+
+#define TRY_BIND_SYMBOL(symbol) \
+	G_STMT_START { \
+		void *sym = dlsym (handle, #symbol); \
+		if (_nm_jansson_ ## symbol && sym != _nm_jansson_ ## symbol) \
+			return FALSE; \
+		_nm_jansson_ ## symbol = sym; \
+	} G_STMT_END
+
+static gboolean
+bind_symbols (void *handle)
+{
+	TRY_BIND_SYMBOL (json_object_iter_value);
+	TRY_BIND_SYMBOL (json_object_key_to_iter);
+	TRY_BIND_SYMBOL (json_integer);
+	TRY_BIND_SYMBOL (json_object_del);
+	TRY_BIND_SYMBOL (json_array_get);
+	TRY_BIND_SYMBOL (json_array_size);
+	TRY_BIND_SYMBOL (json_array_append_new);
+	TRY_BIND_SYMBOL (json_string);
+	TRY_BIND_SYMBOL (json_object_iter_next);
+	TRY_BIND_SYMBOL (json_loads);
+	TRY_BIND_SYMBOL (json_dumps);
+	TRY_BIND_SYMBOL (json_object_iter_key);
+	TRY_BIND_SYMBOL (json_object);
+	TRY_BIND_SYMBOL (json_object_get);
+	TRY_BIND_SYMBOL (json_array);
+	TRY_BIND_SYMBOL (json_false);
+	TRY_BIND_SYMBOL (json_delete);
+	TRY_BIND_SYMBOL (json_true);
+	TRY_BIND_SYMBOL (json_object_size);
+	TRY_BIND_SYMBOL (json_object_set_new);
+	TRY_BIND_SYMBOL (json_object_iter);
+	TRY_BIND_SYMBOL (json_object_iter_at);
+	TRY_BIND_SYMBOL (json_integer_value);
+	TRY_BIND_SYMBOL (json_string_value);
+
+	return TRUE;
+}
+
+gboolean
+nm_jansson_load (void)
+{
+	static enum {
+		UNKNOWN,
+		AVAILABLE,
+		MISSING,
+	} state = UNKNOWN;
+	void *handle;
+
+	if (G_LIKELY (state != UNKNOWN))
+		goto out;
+
+	/* First just resolve the symbols to see if there's a conflict already. */
+	if (!bind_symbols (RTLD_DEFAULT))
+		goto out;
+
+	handle = dlopen (JANSSON_SONAME, RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND);
+	if (!handle)
+		goto out;
+
+	/* Now do the actual binding. */
+	if (!bind_symbols (handle))
+		goto out;
+
+	state = AVAILABLE;
+out:
+	return state == AVAILABLE;
+}
diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h
new file mode 100644
index 00000000..513b8d36
--- /dev/null
+++ b/libnm-core/nm-json.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017, 2018 Red Hat, Inc.
+ */
+#ifndef __NM_JSON_H__
+#define __NM_JSON_H__
+
+gboolean nm_jansson_load (void);
+
+#ifndef NM_JANSSON_C
+#define json_object_iter_value  (*_nm_jansson_json_object_iter_value)
+#define json_object_key_to_iter (*_nm_jansson_json_object_key_to_iter)
+#define json_integer            (*_nm_jansson_json_integer)
+#define json_object_del         (*_nm_jansson_json_object_del)
+#define json_array_get          (*_nm_jansson_json_array_get)
+#define json_array_size         (*_nm_jansson_json_array_size)
+#define json_array_append_new   (*_nm_jansson_json_array_append_new)
+#define json_string             (*_nm_jansson_json_string)
+#define json_object_iter_next   (*_nm_jansson_json_object_iter_next)
+#define json_loads              (*_nm_jansson_json_loads)
+#define json_dumps              (*_nm_jansson_json_dumps)
+#define json_object_iter_key    (*_nm_jansson_json_object_iter_key)
+#define json_object             (*_nm_jansson_json_object)
+#define json_object_get         (*_nm_jansson_json_object_get)
+#define json_array              (*_nm_jansson_json_array)
+#define json_false              (*_nm_jansson_json_false)
+#define json_delete             (*_nm_jansson_json_delete)
+#define json_true               (*_nm_jansson_json_true)
+#define json_object_size        (*_nm_jansson_json_object_size)
+#define json_object_set_new     (*_nm_jansson_json_object_set_new)
+#define json_object_iter        (*_nm_jansson_json_object_iter)
+#define json_object_iter_at     (*_nm_jansson_json_object_iter_at)
+#define json_integer_value      (*_nm_jansson_json_integer_value)
+#define json_string_value       (*_nm_jansson_json_string_value)
+#endif
+
+#endif /* __NM_JSON_H__ */
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 26e7f505..d9c0cbb1 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -191,7 +191,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (priv->dhcp_client_id && !strlen (priv->dhcp_client_id)) {
+	if (priv->dhcp_client_id && !priv->dhcp_client_id[0]) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
 		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-tc-config.h b/libnm-core/nm-setting-tc-config.h
index 736d5ad1..8117d5ea 100644
--- a/libnm-core/nm-setting-tc-config.h
+++ b/libnm-core/nm-setting-tc-config.h
@@ -28,7 +28,6 @@
 
 G_BEGIN_DECLS
 
-NM_AVAILABLE_IN_1_10_2
 typedef struct NMTCQdisc NMTCQdisc;
 
 NM_AVAILABLE_IN_1_10_2
@@ -61,7 +60,6 @@ void        nm_tc_qdisc_set_handle           (NMTCQdisc *qdisc,
 NM_AVAILABLE_IN_1_10_2
 guint32     nm_tc_qdisc_get_parent           (NMTCQdisc *qdisc);
 
-NM_AVAILABLE_IN_1_10_2
 typedef struct NMTCAction NMTCAction;
 
 NM_AVAILABLE_IN_1_10_2
@@ -96,7 +94,6 @@ void        nm_tc_action_set_attribute       (NMTCAction *action,
                                               const char *name,
                                               GVariant *value);
 
-NM_AVAILABLE_IN_1_10_2
 typedef struct NMTCTfilter NMTCTfilter;
 
 NM_AVAILABLE_IN_1_10_2
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index d3688278..60bdf5f6 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -249,7 +249,7 @@ nm_team_link_watcher_new_arp_ping (gint init_wait,
 		return NULL;
 	}
 
-	if (strpbrk (target_host, " \\/\t=\"\'")) {
+	if (strpbrk (source_host, " \\/\t=\"\'")) {
 		g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
 		             _("source-host '%s' contains invalid characters"), source_host);
 		return NULL;
@@ -1485,8 +1485,10 @@ set_property (GObject *object, guint prop_id,
 		break;
 	}
 
-	if (align_config)
+	if (align_config) {
 		_nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value);
+		_align_team_properties (setting);
+	}
 }
 
 static void
@@ -1654,6 +1656,10 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 * Corresponds to the teamd runner.name.
 	 * Permitted values are: "roundrobin", "broadcast", "activebackup",
 	 * "loadbalance", "lacp".
+	 * When setting the runner, all the properties specific to the runner
+	 * will be reset to the default value; all the properties specific to
+	 * other runners will be set to an empty value (or if not possible to
+	 * a default value).
 	 *
 	 * Since: 1.10.2
 	 **/
diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h
index 45207836..d423901f 100644
--- a/libnm-core/nm-setting-team.h
+++ b/libnm-core/nm-setting-team.h
@@ -150,7 +150,7 @@ NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher
 #define NM_SETTING_TEAM_RUNNER_DEFAULT                      NM_SETTING_TEAM_RUNNER_ROUNDROBIN
 #define NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT        NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL
 #define NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT 50
-#define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT             255
+#define NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT             65535
 #define NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT    NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO
 
 
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
index 6b42e0c7..34607252 100644
--- a/libnm-core/nm-setting-vpn.c
+++ b/libnm-core/nm-setting-vpn.c
@@ -614,17 +614,15 @@ need_secrets (NMSetting *setting)
 }
 
 static gboolean
-compare_one_secret (NMSettingVpn *a,
-                    NMSettingVpn *b,
-                    NMSettingCompareFlags flags)
+_compare_secrets (NMSettingVpn *a,
+                  NMSettingVpn *b,
+                  NMSettingCompareFlags flags)
 {
-	GHashTable *a_secrets, *b_secrets;
+	GHashTable *a_secrets;
 	GHashTableIter iter;
 	const char *key, *val;
 
 	a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets;
-	b_secrets = NM_SETTING_VPN_GET_PRIVATE (b)->secrets;
-
 	g_hash_table_iter_init (&iter, a_secrets);
 	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
 		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
@@ -654,6 +652,19 @@ compare_one_secret (NMSettingVpn *a,
 }
 
 static gboolean
+compare_one_secret (NMSettingVpn *a,
+                    NMSettingVpn *b,
+                    NMSettingCompareFlags flags)
+{
+	if (!_compare_secrets (a, b, flags))
+		return FALSE;
+	if (!_compare_secrets (b, a, flags))
+		return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
 compare_property (NMSetting *setting,
                   NMSetting *other,
                   const GParamSpec *prop_spec,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 6cc92ec8..ebbbfd3a 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -36,6 +36,7 @@
 #include <linux/pkt_sched.h>
 
 #if WITH_JANSSON
+#include "nm-json.h"
 #include <jansson.h>
 #endif
 
@@ -650,36 +651,82 @@ _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer ne
 }
 
 gssize
-_nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data)
-{
-	gssize imin, imax, imid;
+_nm_utils_ptrarray_find_binary_search (gconstpointer *list,
+                                       gsize len,
+                                       gconstpointer needle,
+                                       GCompareDataFunc cmpfcn,
+                                       gpointer user_data,
+                                       gssize *out_idx_first,
+                                       gssize *out_idx_last)
+{
+	gssize imin, imax, imid, i2min, i2max, i2mid;
 	int cmp;
 
 	g_return_val_if_fail (list || !len, ~((gssize) 0));
 	g_return_val_if_fail (cmpfcn, ~((gssize) 0));
 
 	imin = 0;
-	if (len == 0)
-		return ~imin;
-
-	imax = len - 1;
-
-	while (imin <= imax) {
-		imid = imin + (imax - imin) / 2;
-
-		cmp = cmpfcn (list[imid], needle, user_data);
-		if (cmp == 0)
-			return imid;
+	if (len > 0) {
+		imax = len - 1;
+
+		while (imin <= imax) {
+			imid = imin + (imax - imin) / 2;
+
+			cmp = cmpfcn (list[imid], needle, user_data);
+			if (cmp == 0) {
+				/* we found a matching entry at index imid.
+				 *
+				 * Does the caller request the first/last index as well (in case that
+				 * there are multiple entries which compare equal). */
+
+				if (out_idx_first) {
+					i2min = imin;
+					i2max = imid + 1;
+					while (i2min <= i2max) {
+						i2mid = i2min + (i2max - i2min) / 2;
+
+						cmp = cmpfcn (list[i2mid], needle, user_data);
+						if (cmp == 0)
+							i2max = i2mid -1;
+						else {
+							nm_assert (cmp < 0);
+							i2min = i2mid + 1;
+						}
+					}
+					*out_idx_first = i2min;
+				}
+				if (out_idx_last) {
+					i2min = imid + 1;
+					i2max = imax;
+					while (i2min <= i2max) {
+						i2mid = i2min + (i2max - i2min) / 2;
+
+						cmp = cmpfcn (list[i2mid], needle, user_data);
+						if (cmp == 0)
+							i2min = i2mid + 1;
+						else {
+							nm_assert (cmp > 0);
+							i2max = i2mid - 1;
+						}
+					}
+					*out_idx_last = i2min - 1;
+				}
+				return imid;
+			}
 
-		if (cmp < 0)
-			imin = imid + 1;
-		else
-			imax = imid - 1;
+			if (cmp < 0)
+				imin = imid + 1;
+			else
+				imax = imid - 1;
+		}
 	}
 
 	/* return the inverse of @imin. This is a negative number, but
 	 * also is ~imin the position where the value should be inserted. */
-	return ~imin;
+	imin = ~imin;
+	NM_SET_OUT (out_idx_first, imin);
+	NM_SET_OUT (out_idx_last, imin);
+	return imin;
 }
 
 gssize
@@ -4845,6 +4892,41 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to)
 
 /*****************************************************************************/
 
+static gboolean
+_nm_utils_is_json_object_no_validation (const char *str, GError **error)
+{
+	if (str) {
+		/* libjansson also requires only utf-8 encoding. */
+		if (!g_utf8_validate (str, -1, NULL)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("not valid utf-8"));
+			return FALSE;
+		}
+		while (g_ascii_isspace (str[0]))
+			str++;
+	}
+
+	/* do some very basic validation to see if this might be a JSON object. */
+	if (str[0] == '{') {
+		gsize l;
+
+		l = strlen (str) - 1;
+		while (l > 0 && g_ascii_isspace (str[l]))
+			l--;
+
+		if (str[l] == '}')
+			return TRUE;
+	}
+
+	g_set_error_literal (error,
+	                     NM_CONNECTION_ERROR,
+	                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+	                     _("is not a JSON object"));
+	return FALSE;
+}
+
 #if WITH_JANSSON
 
 /* Added in Jansson v2.3 (released Jan 27 2012) */
@@ -5151,9 +5233,9 @@ _json_team_normalize_defaults (json_t *json, gboolean reset)
 		_json_delete_object_on_string_match (json, "runner", "hwaddr_policy", NULL,
 		                                     NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_DEFAULT);
 	} else if (nm_streq (runner, NM_SETTING_TEAM_RUNNER_LACP)) {
-		runner_tx_balancer_interval = 50;
+		runner_tx_balancer_interval = NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL_DEFAULT;
 		runner_active = TRUE;
-		runner_sys_prio = 255;
+		runner_sys_prio = NM_SETTING_TEAM_RUNNER_SYS_PRIO_DEFAULT;
 		runner_min_ports = 0;
 		_json_delete_object_on_string_match (json, "runner", "agg_select_policy", NULL,
 		                                     NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_DEFAULT);
@@ -5185,13 +5267,16 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element)
 	g_return_val_if_fail (json_element, NULL);
 
 	json_object_foreach (json_element, j_key, j_val) {
-		if (nm_streq (j_key, "name"))
+		if (nm_streq (j_key, "name")) {
+			g_free (name);
 			name = strdup (json_string_value (j_val));
-		else if (nm_streq (j_key, "target_host"))
+		} else if (nm_streq (j_key, "target_host")) {
+			g_free (target_host);
 			target_host = strdup (json_string_value (j_val));
-		else if (nm_streq (j_key, "source_host"))
+		} else if (nm_streq (j_key, "source_host")) {
+			g_free (source_host);
 			source_host = strdup (json_string_value (j_val));
-		else if (NM_IN_STRSET (j_key, "delay_up", "init_wait"))
+		} else if (NM_IN_STRSET (j_key, "delay_up", "init_wait"))
 			val1 = json_integer_value (j_val);
 		else if (NM_IN_STRSET (j_key, "delay_down", "interval"))
 			val2 = json_integer_value (j_val);
@@ -5314,6 +5399,9 @@ nm_utils_is_json_object (const char *str, GError **error)
 		return FALSE;
 	}
 
+	if (!nm_jansson_load ())
+		return _nm_utils_is_json_object_no_validation (str, error);
+
 	json = json_loads (str, JSON_REJECT_DUPLICATES, &jerror);
 	if (!json) {
 		g_set_error (error,
@@ -5364,6 +5452,8 @@ _nm_utils_team_config_equal (const char *conf1,
 
 	if (nm_streq0 (conf1, conf2))
 		return TRUE;
+	else if (!nm_jansson_load ())
+		return FALSE;
 
 	/* A NULL configuration is equivalent to default value '{}' */
 	json1 = json_loads (conf1 ?: "{}", JSON_REJECT_DUPLICATES, &jerror);
@@ -5421,6 +5511,9 @@ _nm_utils_team_config_get (const char *conf,
 	if (!key)
 		return NULL;
 
+	if (!nm_jansson_load ())
+		return NULL;
+
 	json = json_loads (conf ?: "{}", JSON_REJECT_DUPLICATES, &jerror);
 
 	/* Invalid json in conf */
@@ -5528,6 +5621,9 @@ _nm_utils_team_config_set (char **conf,
 
 	g_return_val_if_fail (key, FALSE);
 
+	if (!nm_jansson_load ())
+		return FALSE;
+
 	json = json_loads (*conf?: "{}", JSON_REJECT_DUPLICATES, &jerror);
 	if (!json)
 		return FALSE;
@@ -5645,19 +5741,6 @@ nm_utils_is_json_object (const char *str, GError **error)
 {
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	if (str) {
-		/* libjansson also requires only utf-8 encoding. */
-		if (!g_utf8_validate (str, -1, NULL)) {
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			                     _("not valid utf-8"));
-			return FALSE;
-		}
-		while (g_ascii_isspace (str[0]))
-			str++;
-	}
-
 	if (!str || !str[0]) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -5666,23 +5749,7 @@ nm_utils_is_json_object (const char *str, GError **error)
 		return FALSE;
 	}
 
-	/* do some very basic validation to see if this might be a JSON object. */
-	if (str[0] == '{') {
-		gsize l;
-
-		l = strlen (str) - 1;
-		while (l > 0 && g_ascii_isspace (str[l]))
-			l--;
-
-		if (str[l] == '}')
-			return TRUE;
-	}
-
-	g_set_error_literal (error,
-	                     NM_CONNECTION_ERROR,
-	                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-	                     _("is not a JSON object"));
-	return FALSE;
+	return _nm_utils_is_json_object_no_validation (str, error);
 }
 
 gboolean
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index d743fd22..520ed581 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -6139,7 +6139,7 @@ static void
 _test_find_binary_search_do (const int *array, gsize len)
 {
 	gsize i;
-	gssize idx;
+	gssize idx, idx_first, idx_last;
 	gs_free gconstpointer *parray = g_new (gconstpointer, len);
 	const int NEEDLE = 0;
 	gconstpointer pneedle = GINT_TO_POINTER (NEEDLE);
@@ -6150,10 +6150,10 @@ _test_find_binary_search_do (const int *array, gsize len)
 
 	expected_result = _nm_utils_ptrarray_find_first (parray, len, pneedle);
 
-	idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL);
-	if (expected_result >= 0)
+	idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
+	if (expected_result >= 0) {
 		g_assert_cmpint (expected_result, ==, idx);
-	else {
+	} else {
 		gssize idx2 = ~idx;
 		g_assert_cmpint (idx, <, 0);
 
@@ -6162,6 +6162,8 @@ _test_find_binary_search_do (const int *array, gsize len)
 		g_assert (idx2 - 1 < 0 || _test_find_binary_search_cmp (parray[idx2 - 1], pneedle, NULL) < 0);
 		g_assert (idx2 >= len || _test_find_binary_search_cmp (parray[idx2], pneedle, NULL) > 0);
 	}
+	g_assert_cmpint (idx, ==, idx_first);
+	g_assert_cmpint (idx, ==, idx_last);
 	for (i = 0; i < len; i++) {
 		int cmp;
 
@@ -6263,6 +6265,94 @@ test_nm_utils_ptrarray_find_binary_search (void)
 }
 
 /*****************************************************************************/
+
+#define BIN_SEARCH_W_DUPS_LEN    100
+#define BIN_SEARCH_W_DUPS_JITTER 10
+
+static int
+_test_bin_search2_cmp (gconstpointer pa,
+                       gconstpointer pb,
+                       gpointer user_data)
+{
+	int a = GPOINTER_TO_INT (pa);
+	int b = GPOINTER_TO_INT (pb);
+
+	g_assert (a >= 0 && a <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER);
+	g_assert (b >= 0 && b <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER);
+	NM_CMP_DIRECT (a, b);
+	return 0;
+}
+
+static int
+_test_bin_search2_cmp_p (gconstpointer pa,
+                         gconstpointer pb,
+                         gpointer user_data)
+{
+	return _test_bin_search2_cmp (*((gpointer *) pa), *((gpointer *) pb), NULL);
+}
+
+static void
+test_nm_utils_ptrarray_find_binary_search_with_duplicates (void)
+{
+	gssize idx, idx2, idx_first2, idx_first, idx_last;
+	int i_test, i_len, i;
+	gssize j;
+	gconstpointer arr[BIN_SEARCH_W_DUPS_LEN];
+	const int N_TEST = 10;
+
+	for (i_test = 0; i_test < N_TEST; i_test++) {
+		for (i_len = 0; i_len < BIN_SEARCH_W_DUPS_LEN; i_len++) {
+
+			/* fill with random numbers... surely there are some duplicates
+			 * there... or maybe even there are none... */
+			for (i = 0; i < i_len; i++)
+				arr[i] = GINT_TO_POINTER (nmtst_get_rand_int () % (i_len + BIN_SEARCH_W_DUPS_JITTER));
+			g_qsort_with_data (arr,
+			                   i_len,
+			                   sizeof (gpointer),
+			                   _test_bin_search2_cmp_p,
+			                   NULL);
+			for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) {
+				gconstpointer p = GINT_TO_POINTER (i);
+
+				idx = _nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
+
+				idx_first2 = _nm_utils_ptrarray_find_first (arr, i_len, p);
+
+				idx2 = _nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
+				g_assert_cmpint (idx, ==, idx2);
+
+				if (idx_first2 < 0) {
+					g_assert_cmpint (idx, <, 0);
+					g_assert_cmpint (idx, ==, idx_first);
+					g_assert_cmpint (idx, ==, idx_last);
+					idx = ~idx;
+					g_assert_cmpint (idx, >=, 0);
+					g_assert_cmpint (idx, <=, i_len);
+					if (i_len == 0)
+						g_assert_cmpint (idx, ==, 0);
+					else {
+						g_assert (idx == i_len || GPOINTER_TO_INT (arr[idx]) > i);
+						g_assert (idx == 0     || GPOINTER_TO_INT (arr[idx - 1]) < i);
+					}
+				} else {
+					g_assert_cmpint (idx_first, ==, idx_first2);
+					g_assert_cmpint (idx_first, >=, 0);
+					g_assert_cmpint (idx_last, <, i_len);
+					g_assert_cmpint (idx_first, <=, idx_last);
+					g_assert_cmpint (idx, >=, idx_first);
+					g_assert_cmpint (idx, <=, idx_last);
+					for (j = idx_first; j < idx_last; j++)
+						g_assert (GPOINTER_TO_INT (arr[j]) == i);
+					g_assert (idx_first == 0 || GPOINTER_TO_INT (arr[idx_first - 1]) < i);
+					g_assert (idx_last == i_len - 1 || GPOINTER_TO_INT (arr[idx_last + 1]) > i);
+				}
+			}
+		}
+	}
+}
+
+/*****************************************************************************/
 static void
 test_nm_utils_enum_from_str_do (GType type, const char *str,
                                 gboolean exp_result, int exp_flags,
@@ -6952,6 +7042,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert);
 	g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array);
 	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
+	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
 	g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
 	g_test_add_func ("/core/general/nm_ptrarray_len", test_nm_ptrarray_len);