about summary refs log tree commit diff
path: root/clients/common/nm-meta-setting-desc.c
diff options
context:
space:
mode:
Diffstat (limited to 'clients/common/nm-meta-setting-desc.c')
-rw-r--r--clients/common/nm-meta-setting-desc.c747
1 files changed, 740 insertions, 7 deletions
diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c
index 1ed6b433..9d0ac906 100644
--- a/clients/common/nm-meta-setting-desc.c
+++ b/clients/common/nm-meta-setting-desc.c
@@ -241,6 +241,150 @@ _parse_ip_route (int family,
 	return route;
 }
 
+static char *
+_dump_team_link_watcher (NMTeamLinkWatcher *watcher)
+{
+	const char *name;
+	NMTeamLinkWatcherArpPingFlags flags;
+	GString *w_dump;
+
+	if (!watcher)
+		return NULL;
+
+	w_dump = g_string_new (NULL);
+	name = nm_team_link_watcher_get_name (watcher);
+	g_string_append_printf (w_dump, "name=%s", name);
+
+#define DUMP_WATCHER_INT(str, watcher, name, key) \
+	G_STMT_START { \
+		int _val = nm_team_link_watcher_get_##key (watcher); \
+		\
+		if (_val) \
+			g_string_append_printf (str, " %s=%d", name, _val); \
+	} G_STMT_END;
+
+	if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
+		DUMP_WATCHER_INT (w_dump, watcher, "delay-up", delay_up);
+		DUMP_WATCHER_INT (w_dump, watcher, "delay-down", delay_down);
+		return g_string_free (w_dump, FALSE);
+	}
+	/* NM_TEAM_LINK_WATCHER_NSNA_PING and NM_TEAM_LINK_WATCHER_ARP_PING */
+	DUMP_WATCHER_INT (w_dump, watcher, "init-wait", init_wait);
+	DUMP_WATCHER_INT (w_dump, watcher, "initerval", interval);
+	DUMP_WATCHER_INT (w_dump, watcher, "missed-max", missed_max);
+#undef DUMP_WATCHER_INT
+	g_string_append_printf (w_dump, " target-host=%s",
+	                        nm_team_link_watcher_get_target_host (watcher));
+
+	if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
+		return g_string_free (w_dump, FALSE);
+
+	g_string_append_printf (w_dump, " source-host=%s",
+	                        nm_team_link_watcher_get_source_host (watcher));
+	flags = nm_team_link_watcher_get_flags (watcher);
+	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE)
+		g_string_append_printf (w_dump, " validate-active=true");
+	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE)
+		g_string_append_printf (w_dump, " validate-inactive=true");
+	if (flags & NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS)
+		g_string_append_printf (w_dump, "send-always=true");
+
+	return g_string_free (w_dump, FALSE);
+}
+
+static NMTeamLinkWatcher *
+_parse_team_link_watcher (const char *str,
+                          GError **error)
+{
+	gs_strfreev char **watcherv = NULL;
+	gs_free char *str_clean = NULL;
+	guint i;
+	gs_free const char *name = NULL;
+	int val1 = 0, val2 = 0, val3 = 3;
+	gs_free const char *target_host = NULL;
+	gs_free const char *source_host = NULL;
+	NMTeamLinkWatcherArpPingFlags flags = 0;
+
+	nm_assert (str);
+	nm_assert (!error || !*error);
+
+	str_clean = g_strstrip (g_strdup (str));
+	watcherv = nmc_strsplit_set (str_clean, " \t", 0);
+	if (!watcherv || !watcherv[0]) {
+		g_set_error (error, 1, 0, "'%s' is not valid", str);
+		return NULL;
+	}
+
+	for (i = 0; watcherv[i]; i++) {
+		gs_strfreev char **pair = NULL;
+
+		pair = nmc_strsplit_set (watcherv[i], "=", 0);
+		if (!pair[0]) {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "properties should be specified as 'key=value'");
+			return NULL;
+		}
+		if (!pair[1]) {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "missing key value");
+			return NULL;
+		}
+		if (pair[2]) {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "properties should be specified as 'key=value'");
+			return NULL;
+		}
+
+		if (nm_streq (pair[0], "name"))
+			name = g_strdup (pair[1]);
+		else if (   nm_streq (pair[0], "delay-up")
+		         || nm_streq (pair[0], "init-wait"))
+			val1 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
+		else if (   nm_streq (pair[0], "delay-down")
+		         || nm_streq (pair[0], "interval"))
+			val2 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
+		else if (nm_streq (pair[0], "missed-max"))
+			val3 = _nm_utils_ascii_str_to_int64 (pair[1], 10, 0, G_MAXINT32, -1);
+		else if (nm_streq (pair[0], "target-host"))
+			target_host = g_strdup (pair[1]);
+		else if (nm_streq (pair[0], "source-host"))
+			source_host = g_strdup (pair[1]);
+		else if (nm_streq (pair[0], "validate-active")) {
+			if (nm_streq (pair[1], "true"))
+				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
+		} else if (nm_streq (pair[0], "validate-inactive")) {
+			if (nm_streq (pair[1], "true"))
+				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE;
+		} else if (nm_streq (pair[0], "send-always")) {
+			if (nm_streq (pair[1], "true"))
+				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
+		} else {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "unknown key");
+			return NULL;
+		}
+
+		if ((val1 < 0) || (val2 < 0) || (val3 < 0)) {
+			g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i],
+			             "value is not a valid number [0, MAXINT]");
+			return NULL;
+		}
+	}
+
+	if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ETHTOOL))
+		return nm_team_link_watcher_new_ethtool (val1, val2, error);
+	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
+		return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, error);
+	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING))
+		return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host, source_host, flags, error);
+
+	if (!name)
+		g_set_error (error, 1, 0, "link watcher name missing");
+	else
+		g_set_error (error, 1, 0, "unknown link watcher name: '%s'", name);
+	return NULL;
+}
+
 /* Max priority values from libnm-core/nm-setting-vlan.c */
 #define MAX_SKB_PRIO   G_MAXUINT32
 #define MAX_8021P_PRIO 7  /* Max 802.1p priority */
@@ -461,6 +605,7 @@ _get_text_hidden (NMMetaAccessorGetType get_type)
 	return NM_META_TEXT_HIDDEN;
 }
 
+
 /*****************************************************************************/
 
 G_GNUC_PRINTF (4, 5)
@@ -2310,6 +2455,9 @@ _complete_fcn_connection_type (ARGS_COMPLETE_FCN)
 		const NMMetaSettingInfoEditor *setting_info = &nm_meta_setting_infos_editor[i];
 		const char *v;
 
+		if (!setting_info->valid_parts)
+			continue;
+
 		v = setting_info->alias;
 		if (v) {
 			if (!text || strncmp (text, v, text_len) == 0)
@@ -2448,7 +2596,7 @@ _complete_fcn_connection_master (ARGS_COMPLETE_FCN)
 			expected_type = nm_setting_connection_get_slave_type (s_con);
 	}
 
-	text_len = strlen (text);
+	text_len = text ? strlen (text) : 0;
 
 	result = g_new (char *, (2 * len) + 1);
 	for (i = 0, j = 0; i < len; i++) {
@@ -2974,14 +3122,14 @@ _get_fcn_ip_config_addresses (ARGS_GET_FCN)
 {
 	NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
 	GString *printable;
-	guint32 num_addresses, i;
+	guint num_addresses, i;
 	NMIPAddress *addr;
 
 	RETURN_UNSUPPORTED_GET_TYPE ();
 
 	printable = g_string_new (NULL);
 
-	num_addresses = nm_setting_ip_config_get_num_addresses (s_ip);
+	num_addresses = NM_MIN ((guint) G_MAXINT, nm_setting_ip_config_get_num_addresses (s_ip));
 	for (i = 0; i < num_addresses; i++) {
 		addr = nm_setting_ip_config_get_address (s_ip, i);
 
@@ -3001,14 +3149,14 @@ _get_fcn_ip_config_routes (ARGS_GET_FCN)
 {
 	NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
 	GString *printable;
-	guint32 num_routes, i;
+	guint num_routes, i;
 	NMIPRoute *route;
 
 	RETURN_UNSUPPORTED_GET_TYPE ();
 
 	printable = g_string_new (NULL);
 
-	num_routes = nm_setting_ip_config_get_num_routes (s_ip);
+	num_routes = NM_MIN ((guint) G_MAXINT, nm_setting_ip_config_get_num_routes (s_ip));
 	for (i = 0; i < num_routes; i++) {
 		gs_free char *attr_str = NULL;
 		gs_strfreev char **attr_names = NULL;
@@ -3465,6 +3613,13 @@ DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_dns_options,
                                _validate_and_remove_ipv6_dns_option)
 
 static gboolean
+_dns_options_is_default (NMSettingIPConfig *setting)
+{
+	return    nm_setting_ip_config_has_dns_options (setting)
+	       && !nm_setting_ip_config_get_num_dns_options (setting);
+}
+
+static gboolean
 _set_fcn_ip6_config_addresses (ARGS_SET_FCN)
 {
 	gs_strfreev char **strv = NULL;
@@ -3632,6 +3787,158 @@ _validate_fcn_proxy_pac_script (const char *value, char **out_to_free, GError **
 	RETURN_STR_TO_FREE (script);
 }
 
+static gconstpointer
+_get_fcn_tc_config_qdiscs (ARGS_GET_FCN)
+{
+	NMSettingTCConfig *s_tc = NM_SETTING_TC_CONFIG (setting);
+	GString *printable;
+	guint num_qdiscs, i;
+	NMTCQdisc *qdisc;
+	char *str;
+
+	RETURN_UNSUPPORTED_GET_TYPE ();
+
+	printable = g_string_new (NULL);
+
+	num_qdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc);
+	for (i = 0; i < num_qdiscs; i++) {
+		qdisc = nm_setting_tc_config_get_qdisc (s_tc, i);
+
+		if (printable->len > 0)
+			g_string_append (printable, ", ");
+
+		str = nm_utils_tc_qdisc_to_str (qdisc, NULL);
+		if (str) {
+			g_string_append (printable, str);
+			g_free (str);
+		}
+	}
+
+	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
+}
+
+static gboolean
+_set_fcn_tc_config_qdiscs (ARGS_SET_FCN)
+{
+	gs_free const char **strv = NULL;
+	const char *const*iter;
+	NMTCQdisc *tc_qdisc;
+	GError *local = NULL;
+
+	strv = nm_utils_strsplit_set (value, ",");
+	for (iter = strv; strv && *iter; iter++) {
+		tc_qdisc = nm_utils_tc_qdisc_from_str (*iter, &local);
+		if (!tc_qdisc) {
+			g_set_error (error, 1, 0, "%s %s", local->message,
+			             _("The valid syntax is: '[root | parent <handle>] [handle <handle>] <qdisc>'"));
+			return FALSE;
+		}
+		nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (setting), tc_qdisc);
+		nm_tc_qdisc_unref (tc_qdisc);
+	}
+	return TRUE;
+}
+
+static gboolean
+_validate_and_remove_tc_qdisc (NMSettingTCConfig *setting,
+                               const char *value,
+                               GError **error)
+{
+	NMTCQdisc *qdisc;
+	gboolean ret;
+
+	qdisc = nm_utils_tc_qdisc_from_str (value, error);
+	if (!qdisc)
+		return FALSE;
+
+	ret = nm_setting_tc_config_remove_qdisc_by_value (setting, qdisc);
+	if (!ret)
+		g_set_error (error, 1, 0, _("the property doesn't contain qdisc '%s'"), value);
+	nm_tc_qdisc_unref (qdisc);
+	return ret;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_tc_config_qdiscs,
+                               NM_SETTING_TC_CONFIG,
+                               nm_setting_tc_config_get_num_qdiscs,
+                               nm_setting_tc_config_remove_qdisc,
+                               _validate_and_remove_tc_qdisc)
+
+static gconstpointer
+_get_fcn_tc_config_tfilters (ARGS_GET_FCN)
+{
+	NMSettingTCConfig *s_tc = NM_SETTING_TC_CONFIG (setting);
+	GString *printable;
+	guint num_tfilters, i;
+	NMTCTfilter *tfilter;
+	char *str;
+
+	RETURN_UNSUPPORTED_GET_TYPE ();
+
+	printable = g_string_new (NULL);
+
+	num_tfilters = nm_setting_tc_config_get_num_tfilters (s_tc);
+	for (i = 0; i < num_tfilters; i++) {
+		tfilter = nm_setting_tc_config_get_tfilter (s_tc, i);
+
+		if (printable->len > 0)
+			g_string_append (printable, ", ");
+
+		str = nm_utils_tc_tfilter_to_str (tfilter, NULL);
+		if (str) {
+			g_string_append (printable, str);
+			g_free (str);
+		}
+	}
+
+	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
+}
+
+static gboolean
+_set_fcn_tc_config_tfilters (ARGS_SET_FCN)
+{
+	gs_free const char **strv = NULL;
+	const char *const*iter;
+	NMTCTfilter *tc_tfilter;
+	GError *local = NULL;
+
+	strv = nm_utils_strsplit_set (value, ",");
+	for (iter = strv; strv && *iter; iter++) {
+		tc_tfilter = nm_utils_tc_tfilter_from_str (*iter, &local);
+		if (!tc_tfilter) {
+			g_set_error (error, 1, 0, "%s %s", local->message,
+			             _("The valid syntax is: '[root | parent <handle>] [handle <handle>] <tfilter>'"));
+			return FALSE;
+		}
+		nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (setting), tc_tfilter);
+		nm_tc_tfilter_unref (tc_tfilter);
+	}
+	return TRUE;
+}
+
+static gboolean
+_validate_and_remove_tc_tfilter (NMSettingTCConfig *setting,
+                               const char *value,
+                               GError **error)
+{
+	NMTCTfilter *tfilter;
+	gboolean ret;
+
+	tfilter = nm_utils_tc_tfilter_from_str (value, error);
+	if (!tfilter)
+		return FALSE;
+
+	ret = nm_setting_tc_config_remove_tfilter_by_value (setting, tfilter);
+	if (!ret)
+		g_set_error (error, 1, 0, _("the property doesn't contain tfilter '%s'"), value);
+	nm_tc_tfilter_unref (tfilter);
+	return ret;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_tc_config_tfilters,
+                               NM_SETTING_TC_CONFIG,
+                               nm_setting_tc_config_get_num_tfilters,
+                               nm_setting_tc_config_remove_tfilter,
+                               _validate_and_remove_tc_tfilter)
+
 static const char *
 _validate_fcn_team_config (const char *value, char **out_to_free, GError **error)
 {
@@ -3642,6 +3949,204 @@ _validate_fcn_team_config (const char *value, char **out_to_free, GError **error
 	RETURN_STR_TO_FREE (json);
 }
 
+static gboolean
+_is_valid_team_runner_tx_hash_element (const char *tx_hash_element)
+{
+	const char *valid_tx_hashes[] = { "eth", "vlan", "ipv4", "ipv6", "ip",
+	                                  "l3", "tcp", "udp", "sctp", "l4", NULL };
+	if (nmc_string_is_valid (tx_hash_element, valid_tx_hashes, NULL))
+		return TRUE;
+	return FALSE;
+}
+
+static gboolean
+_set_fcn_team_runner_tx_hash (ARGS_SET_FCN)
+{
+	char **strv = NULL;
+	guint i = 0;
+
+	g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+	strv = _nm_utils_strv_cleanup (g_strsplit_set (value, " \t,", 0),
+	                               TRUE, TRUE, TRUE);
+	if (!verify_string_list (strv, property_info->property_name,
+	                         _is_valid_team_runner_tx_hash_element,
+	                         error)) {
+		g_strfreev (strv);
+		return FALSE;
+	}
+
+	while (strv && strv[i])
+		nm_setting_team_add_runner_tx_hash (NM_SETTING_TEAM (setting), strv[i++]);
+	g_strfreev (strv);
+
+	return TRUE;
+}
+
+static gboolean
+_validate_and_remove_team_runner_tx_hash (NMSettingTeam *setting,
+                                         const char *tx_hash,
+                                         GError **error)
+{
+	if (!nm_setting_team_remove_runner_tx_hash_by_value (setting, tx_hash)) {
+		g_set_error (error, 1, 0,
+		             _("the property doesn't contain string '%s'"),
+		             tx_hash);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_runner_tx_hash,
+                               NM_SETTING_TEAM,
+                               nm_setting_team_get_num_runner_tx_hash,
+                               nm_setting_team_remove_runner_tx_hash,
+                               _validate_and_remove_team_runner_tx_hash)
+
+static gconstpointer
+_get_fcn_team_link_watchers (ARGS_GET_FCN)
+{
+	NMSettingTeam *s_team = NM_SETTING_TEAM (setting);
+	GString *printable;
+	guint num_watchers, i;
+	NMTeamLinkWatcher *watcher;
+	char *watcher_str;
+
+	RETURN_UNSUPPORTED_GET_TYPE ();
+
+	printable = g_string_new (NULL);
+
+	num_watchers = nm_setting_team_get_num_link_watchers (s_team);
+	for (i = 0; i < num_watchers; i++) {
+		watcher = nm_setting_team_get_link_watcher (s_team, i);
+		watcher_str = _dump_team_link_watcher (watcher);
+		if (watcher_str) {
+			if (printable->len > 0)
+				g_string_append (printable, ", ");
+			g_string_append (printable, watcher_str);
+			g_free (watcher_str);
+		}
+	}
+
+	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
+}
+
+static gboolean
+_set_fcn_team_link_watchers (ARGS_SET_FCN)
+{
+	gs_strfreev char **strv = NULL;
+	const char *const*iter;
+	NMTeamLinkWatcher *watcher;
+
+	strv = nmc_strsplit_set (value, ",", 0);
+	for (iter = (const char *const*) strv; *iter; iter++) {
+		watcher = _parse_team_link_watcher (*iter, error);
+		if (!watcher)
+			return FALSE;
+		nm_setting_team_add_link_watcher (NM_SETTING_TEAM (setting), watcher);
+		nm_team_link_watcher_unref (watcher);
+	}
+	return TRUE;
+}
+
+static gboolean
+_validate_and_remove_team_link_watcher (NMSettingTeam *setting,
+                                        const char *watcher_str,
+                                        GError **error)
+{
+	NMTeamLinkWatcher *watcher;
+	gboolean ret;
+
+	watcher = _parse_team_link_watcher (watcher_str, error);
+	if (!watcher)
+		return FALSE;
+
+	ret = nm_setting_team_remove_link_watcher_by_value (setting, watcher);
+	if (!ret) {
+		g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"),
+		             watcher_str);
+	}
+	nm_team_link_watcher_unref (watcher);
+	return ret;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_link_watchers,
+                               NM_SETTING_TEAM,
+                               nm_setting_team_get_num_link_watchers,
+                               nm_setting_team_remove_link_watcher,
+                               _validate_and_remove_team_link_watcher)
+
+static gconstpointer
+_get_fcn_team_port_link_watchers (ARGS_GET_FCN)
+{
+	NMSettingTeamPort *s_team_port = NM_SETTING_TEAM_PORT (setting);
+	GString *printable;
+	guint num_watchers, i;
+	NMTeamLinkWatcher *watcher;
+	char *watcher_str;
+
+	RETURN_UNSUPPORTED_GET_TYPE ();
+
+	printable = g_string_new (NULL);
+
+	num_watchers = nm_setting_team_port_get_num_link_watchers (s_team_port);
+	for (i = 0; i < num_watchers; i++) {
+		watcher = nm_setting_team_port_get_link_watcher (s_team_port, i);
+		watcher_str = _dump_team_link_watcher (watcher);
+		if (watcher_str) {
+			if (printable->len > 0)
+				g_string_append (printable, ", ");
+			g_string_append (printable, watcher_str);
+			g_free (watcher_str);
+		}
+	}
+
+	RETURN_STR_TO_FREE (g_string_free (printable, FALSE));
+}
+
+static gboolean
+_set_fcn_team_port_link_watchers (ARGS_SET_FCN)
+{
+	gs_strfreev char **strv = NULL;
+	const char *const*iter;
+	NMTeamLinkWatcher *watcher;
+
+	strv = nmc_strsplit_set (value, ",", 0);
+	for (iter = (const char *const*) strv; *iter; iter++) {
+		watcher = _parse_team_link_watcher (*iter, error);
+		if (!watcher)
+			return FALSE;
+		nm_setting_team_port_add_link_watcher (NM_SETTING_TEAM_PORT (setting), watcher);
+		nm_team_link_watcher_unref (watcher);
+	}
+	return TRUE;
+}
+
+static gboolean
+_validate_and_remove_team_port_link_watcher (NMSettingTeamPort *setting,
+                                             const char *watcher_str,
+                                             GError **error)
+{
+	NMTeamLinkWatcher *watcher;
+	gboolean ret;
+
+	watcher = _parse_team_link_watcher (watcher_str, error);
+	if (!watcher)
+		return FALSE;
+
+	ret = nm_setting_team_port_remove_link_watcher_by_value (setting, watcher);
+	if (!ret) {
+		g_set_error (error, 1, 0, _("the property doesn't contain link watcher '%s'"),
+		             watcher_str);
+	}
+	nm_team_link_watcher_unref (watcher);
+	return ret;
+}
+DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_team_port_link_watchers,
+                               NM_SETTING_TEAM_PORT,
+                               nm_setting_team_port_get_num_link_watchers,
+                               nm_setting_team_port_remove_link_watcher,
+                               _validate_and_remove_team_port_link_watcher)
+
 static gconstpointer
 _get_fcn_vlan_flags (ARGS_GET_FCN)
 {
@@ -4440,6 +4945,24 @@ static const NMMetaPropertyType _pt_gobject_devices = {
 	   "{ \"device\": \"team0\", \"runner\": {\"name\": \"roundrobin\"}, \"ports\": {\"eth1\": {}, \"eth2\": {}} }\n" \
 	   "          set team.config /etc/my-team.conf\n")
 
+#define TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE \
+	N_("Enter a list of link watchers formatted as dictionaries where the keys " \
+	   "are teamd properties. Dictionary pairs are in the form: key=value and pairs " \
+	   "are separated by ' '. Dictionaries are separated with ','.\n" \
+	   "The keys allowed/required in the dictionary change on the basis of the link " \
+	   "watcher type, while the only property common to all the link watchers is " \
+	   " 'name'*, which defines the link watcher to be specified.\n\n" \
+	   "Properties available for the 'ethtool' link watcher:\n" \
+	   "  'delay-up', 'delay-down'\n\n" \
+	   "Properties available for the 'nsna_ping' link watcher:\n" \
+	   "  'init-wait', 'interval', 'missed-max', 'target-host'*\n\n" \
+	   "Properties available for the 'arp_ping' include all the ones for 'nsna_ping' and:\n" \
+	   "  'source-host', 'validate-active', 'validate-inactive', 'send-always'.\n\n" \
+	   "Properties flagged with a '*' are mandatory.\n\n" \
+	   "Example:\n" \
+	   "   name=arp_ping,source-host=172.16.1.1,target-host=172.16.1.254; name=ethtool,delay-up=3\n")
+
+
 #define DEFINE_DCB_PROPRITY_PROPERTY_TYPE \
 		.property_type =                &_pt_gobject_int, \
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int, \
@@ -5310,7 +5833,7 @@ static const NMMetaPropertyInfo *const property_infos_IP4_CONFIG[] = {
 			.remove_fcn =               _remove_fcn_ipv4_config_dns_options,
 		),
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (get_with_default,
-			.fcn =                      GET_FCN_WITH_DEFAULT (NMSettingIPConfig, nm_setting_ip_config_has_dns_options),
+			.fcn =                      GET_FCN_WITH_DEFAULT (NMSettingIPConfig, _dns_options_is_default),
 		),
 	),
 	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_PRIORITY, DESCRIBE_DOC_NM_SETTING_IP4_CONFIG_DNS_PRIORITY,
@@ -5473,7 +5996,7 @@ static const NMMetaPropertyInfo *const property_infos_IP6_CONFIG[] = {
 			.remove_fcn =               _remove_fcn_ipv6_config_dns_options,
 		),
 		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (get_with_default,
-			.fcn =     GET_FCN_WITH_DEFAULT (NMSettingIPConfig, nm_setting_ip_config_has_dns_options),
+			.fcn =     GET_FCN_WITH_DEFAULT (NMSettingIPConfig, _dns_options_is_default),
 		),
 	),
 	PROPERTY_INFO (NM_SETTING_IP_CONFIG_DNS_PRIORITY, DESCRIBE_DOC_NM_SETTING_IP6_CONFIG_DNS_PRIORITY,
@@ -5988,6 +6511,26 @@ static const NMMetaPropertyInfo *const property_infos_PROXY[] = {
 };
 
 #undef  _CURRENT_NM_META_SETTING_TYPE
+#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TC_CONFIG
+static const NMMetaPropertyInfo *const property_infos_TC_CONFIG[] = {
+	PROPERTY_INFO (NM_SETTING_TC_CONFIG_QDISCS, DESCRIBE_DOC_NM_SETTING_TC_CONFIG_QDISCS,
+		.property_type = DEFINE_PROPERTY_TYPE (
+			.get_fcn =                  _get_fcn_tc_config_qdiscs,
+			.set_fcn =                  _set_fcn_tc_config_qdiscs,
+			.remove_fcn =               _remove_fcn_tc_config_qdiscs,
+		),
+	),
+	PROPERTY_INFO (NM_SETTING_TC_CONFIG_TFILTERS, DESCRIBE_DOC_NM_SETTING_TC_CONFIG_TFILTERS,
+		.property_type = DEFINE_PROPERTY_TYPE (
+			.get_fcn =                  _get_fcn_tc_config_tfilters,
+			.set_fcn =                  _set_fcn_tc_config_tfilters,
+			.remove_fcn =               _remove_fcn_tc_config_tfilters,
+		),
+	),
+	NULL
+};
+
+#undef  _CURRENT_NM_META_SETTING_TYPE
 #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_TEAM
 static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
 	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_CONFIG,
@@ -6000,6 +6543,138 @@ static const NMMetaPropertyInfo *const property_infos_TEAM[] = {
 			.validate_fcn =             _validate_fcn_team_config,
 		),
 	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "disabled",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "disabled",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER,
+		.property_type =                &_pt_gobject_string,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_BROADCAST,
+			                                           NM_SETTING_TEAM_RUNNER_ROUNDROBIN,
+			                                           NM_SETTING_TEAM_RUNNER_ACTIVEBACKUP,
+			                                           NM_SETTING_TEAM_RUNNER_LOADBALANCE,
+			                                           NM_SETTING_TEAM_RUNNER_LACP),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
+		.property_type =                &_pt_gobject_string,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_SAME_ALL,
+			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_BY_ACTIVE,
+			                                           NM_SETTING_TEAM_RUNNER_HWADDR_POLICY_ONLY_ACTIVE),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_HASH,
+		.property_type =  DEFINE_PROPERTY_TYPE (
+			.get_fcn =                   _get_fcn_gobject,
+			.set_fcn =                   _set_fcn_team_runner_tx_hash,
+			.remove_fcn =                _remove_fcn_team_runner_tx_hash,
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER,
+		.property_type =                &_pt_gobject_string,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			.values_static =            VALUES_STATIC ("basic"),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 50,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_ACTIVE,
+		.property_type =                & _pt_gobject_bool,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_FAST_RATE,
+		.property_type =                & _pt_gobject_bool,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_SYS_PRIO,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 255,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_MIN_PORTS,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+		        .value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
+		.property_type =                &_pt_gobject_string,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
+			.values_static =            VALUES_STATIC (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO,
+			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_LACP_PRIO_STABLE,
+			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_BANDWIDTH,
+			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_COUNT,
+			                                           NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY_PORT_CONFIG),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_LINK_WATCHERS,
+		.describe_message =             TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE,
+		.property_type = DEFINE_PROPERTY_TYPE (
+			.get_fcn =                  _get_fcn_team_link_watchers,
+			.set_fcn =                  _set_fcn_team_link_watchers,
+			.remove_fcn =               _remove_fcn_team_link_watchers,
+		),
+	),
 	NULL
 };
 
@@ -6016,6 +6691,61 @@ static const NMMetaPropertyInfo *const property_infos_TEAM_PORT[] = {
 			.validate_fcn =             _validate_fcn_team_config,
 		),
 	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_QUEUE_ID,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+			.value_infos =          INT_VALUE_INFOS (
+				{
+					.value = -1,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_PRIO,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+			.value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_STICKY,
+		.property_type =                &_pt_gobject_bool,
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_PRIO,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+			.value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 255,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LACP_KEY,
+		.property_type =                &_pt_gobject_int,
+		.property_typ_data = DEFINE_PROPERTY_TYP_DATA_SUBTYPE (gobject_int,
+			.value_infos =          INT_VALUE_INFOS (
+				{
+					.value = 0,
+					.nick = "default",
+				}
+			),
+		),
+	),
+	PROPERTY_INFO_WITH_DESC (NM_SETTING_TEAM_PORT_LINK_WATCHERS,
+		.describe_message =             TEAM_LINK_WATCHERS_DESCRIBE_MESSAGE,
+		.property_type = DEFINE_PROPERTY_TYPE (
+			.get_fcn =                  _get_fcn_team_port_link_watchers,
+			.set_fcn =                  _set_fcn_team_port_link_watchers,
+			.remove_fcn =               _remove_fcn_team_port_link_watchers,
+		),
+	),
 	NULL
 };
 
@@ -6810,6 +7540,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN)
 #define SETTING_PRETTY_NAME_PPPOE               N_("PPPoE")
 #define SETTING_PRETTY_NAME_PROXY               N_("Proxy")
 #define SETTING_PRETTY_NAME_SERIAL              N_("Serial settings")
+#define SETTING_PRETTY_NAME_TC_CONFIG           N_("Traffic controls")
 #define SETTING_PRETTY_NAME_TEAM                N_("Team device")
 #define SETTING_PRETTY_NAME_TEAM_PORT           N_("Team port")
 #define SETTING_PRETTY_NAME_TUN                 N_("Tun device")
@@ -6995,6 +7726,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = {
 		.setting_init_fcn =             _setting_init_fcn_proxy,
 	),
 	SETTING_INFO (SERIAL),
+	SETTING_INFO (TC_CONFIG),
 	SETTING_INFO (TEAM,
 		.valid_parts = NM_META_SETTING_VALID_PARTS (
 			NM_META_SETTING_VALID_PART_ITEM (CONNECTION,            TRUE),
@@ -7075,6 +7807,7 @@ const NMMetaSettingValidPartItem *const nm_meta_setting_info_valid_parts_default
 static const NMMetaSettingValidPartItem *const valid_settings_noslave[] = {
 	NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE),
 	NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE),
+	NM_META_SETTING_VALID_PART_ITEM (TC_CONFIG,  FALSE),
 	NM_META_SETTING_VALID_PART_ITEM (PROXY,      FALSE),
 	NULL,
 };