static void
validate (GtkWidget *dialog)
{
	GtkBuilder *builder;
	GtkWidget *widget;
	GtkTreeModel *model;
	GtkTreeIter tree_iter;
	gboolean valid = FALSE, iter_valid = FALSE;

	g_return_if_fail (dialog != NULL);

	builder = g_object_get_data (G_OBJECT (dialog), "builder");
	g_return_if_fail (builder != NULL);
	g_return_if_fail (GTK_IS_BUILDER (builder));

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_routes"));
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
	iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);

	while (iter_valid) {
		struct in6_addr dest, next_hop;
		guint prefix = 0, metric = 0;

		/* Address */
		if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest))
			goto done;

		/* Prefix */
		if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix))
			goto done;

		/* Next hop (optional) */
		if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop))
			goto done;

		/* Metric (optional) */
		if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric))
			goto done;

		iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
	}
	valid = TRUE;

done:
	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
	gtk_widget_set_sensitive (widget, valid);
}
Example #2
0
static void
validate (GtkWidget *dialog)
{
	GtkBuilder *builder;
	GtkWidget *widget;
	GtkTreeModel *model;
	GtkTreeIter tree_iter;
	gboolean valid = FALSE, iter_valid = FALSE;

	g_return_if_fail (dialog != NULL);

	builder = g_object_get_data (G_OBJECT (dialog), "builder");
	g_return_if_fail (builder != NULL);
	g_return_if_fail (GTK_IS_BUILDER (builder));

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip4_routes"));
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
	iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);

	while (iter_valid) {
		guint32 addr = 0, prefix = 0, next_hop = 0, metric = 0;

		/* Address */
		if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &addr))
			goto done;
		/* Don't allow inserting 0.0.0.0 for now - that's not supported in libnm-util */
		if (addr == 0)
			goto done;

		/* Prefix */
		if (!get_one_prefix (model, &tree_iter, COL_PREFIX, TRUE, &prefix))
			goto done;
		/* Don't allow zero prefix for now - that's not supported in libnm-util */
		if (prefix == 0)
			goto done;

		/* Next hop (optional) */
		if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop))
			goto done;

		/* Metric (optional) */
		if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric))
			goto done;

		iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
	}
	valid = TRUE;

done:
	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
	gtk_widget_set_sensitive (widget, valid);
}
Example #3
0
static gpointer
build_ip4_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route)
{
	GArray *result;
	guint32 addr;
	guint32 address = 0;
	guint32 gateway = 0;
	guint32 metric = 0;
	int err;

	g_return_val_if_fail (address_str, NULL);

	/* Address */
	err = inet_pton (AF_INET, address_str, &addr);
	if (err <= 0) {
		g_warning ("%s: ignoring invalid IPv4 address '%s'", __func__, address_str);
		return NULL;
	}
	address = addr;

	/* Gateway */
	if (gateway_str && gateway_str[0]) {
		err = inet_pton (AF_INET, gateway_str, &addr);
		if (err <= 0) {
			g_warning ("%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str);
			return NULL;
		}
		gateway = addr;
	}
	else
		gateway = 0;

	/* parse metric, default to 0 */
	if (metric_str) {
		if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
			return NULL;
	}

	result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3 + !!route);
	g_array_append_val (result, address);
	g_array_append_val (result, plen);
	g_array_append_val (result, gateway);
	if (route)
		g_array_append_val (result, metric);

	return result;
}
Example #4
0
/* The following IPv4 and IPv6 address formats are supported:
 *
 * address (DEPRECATED)
 * address/plen
 * address/gateway (DEPRECATED)
 * address/plen/gateway
 *
 * The following IPv4 and IPv6 route formats are supported:
 *
 * address/plen (NETWORK dev DEVICE)
 * address/plen/gateway (NETWORK via GATEWAY dev DEVICE)
 * address/plen//gateway (NETWORK dev DEVICE metric METRIC)
 * address/plen/gateway/metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
 *
 * For backward, forward and sideward compatibility, slash (/),
 * semicolon (;) and comma (,) are interchangable. The use of
 * slash in the above examples is therefore not significant.
 *
 * Leaving out the prefix length is discouraged and DEPRECATED. The
 * default value of IPv6 prefix length was 64 and has not been
 * changed. The default for IPv4 is now 24, which is the closest
 * IPv4 equivalent. These defaults may just as well be changed to
 * match the iproute2 defaults (32 for IPv4 and 128 for IPv6).
 *
 * The returned result is GArray for IPv4 and GValueArray for IPv6.
 */
static gpointer
read_one_ip_address_or_route (GKeyFile *file,
	const char *setting_name,
	const char *key_name,
	gboolean ipv6,
	gboolean route)
{
	guint32 plen, metric;
	gpointer result;
	char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;

	current = value = g_key_file_get_string (file, setting_name, key_name, NULL);
	if (!value)
		return NULL;

	/* get address field */
	address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
	if (error) {
		g_warning ("keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
			*error, setting_name, key_name, error - current, current);
		goto error;
	}
	/* get prefix length field (skippable) */
	plen_str = read_field (&current, &error, DIGITS, DELIMITERS);
	/* get gateway field */
	gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
	if (error) {
		g_warning ("keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
			*error, setting_name, key_name,
			plen_str ? "gateway" : "gateway or prefix length",
			error - current, current);
		goto error;
	}
	/* for routes, get metric */
	if (route) {
		metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
		if (error) {
			g_warning ("keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
				*error, setting_name, key_name, error - current, current);
			goto error;
		}
	} else
		metric_str = NULL;
	if (current) {
		/* there is still some data */
		if (*current) {
			/* another field follows */
			g_warning ("keyfile: %s.%s: Garbage at the and of the line: %s",
				setting_name, key_name, current);
			goto error;
		} else {
			/* semicolon at the end of input */
			g_message ("keyfile: %s.%s: Deprecated semicolon at the end of value.",
				setting_name, key_name);
		}
	}

	/* parse plen, fallback to defaults */
	if (plen_str)
		g_return_val_if_fail (get_one_int (plen_str, ipv6 ? 128 : 32,
			key_name, &plen), NULL);
	else {
		if (route)
			plen = ipv6 ? 128 : 24;
		else
			plen = ipv6 ? 64 : 24;
		g_warning ("keyfile: Missing prefix length in '%s.%s', defaulting to %d",
			setting_name, key_name, plen);
	}

	/* parse metric, default to 0 */
	metric = 0;
	if (metric_str)
		g_return_val_if_fail (get_one_int (metric_str, G_MAXUINT32,
			key_name, &metric), NULL);

	/* build the appropriate data structure for NetworkManager settings */
	if (route)
		g_debug ("keyfile: %s.%s: route %s/%d gateway %s metric %d", setting_name, key_name, address_str, plen, gateway_str, metric);
	else
		g_debug ("keyfile: %s.%s: address %s/%d gateway %s", setting_name, key_name, address_str, plen, gateway_str);
	result = (ipv6 ? build_ip6_address_or_route : build_ip4_address_or_route) (
		address_str, plen, gateway_str, metric, route);

	g_free (value);
	return result;
error:
	g_free (value);
	return NULL;
}
void
ip6_routes_dialog_update_setting (GtkWidget *dialog, NMSettingIP6Config *s_ip6)
{
	GtkBuilder *builder;
	GtkWidget *widget;
	GtkTreeModel *model;
	GtkTreeIter tree_iter;
	gboolean iter_valid;

	g_return_if_fail (dialog != NULL);
	g_return_if_fail (s_ip6 != NULL);

	builder = g_object_get_data (G_OBJECT (dialog), "builder");
	g_return_if_fail (builder != NULL);
	g_return_if_fail (GTK_IS_BUILDER (builder));

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_routes"));
	model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
	iter_valid = gtk_tree_model_get_iter_first (model, &tree_iter);

	nm_setting_ip6_config_clear_routes (s_ip6);

	while (iter_valid) {
		struct in6_addr dest, next_hop;
		guint prefix = 0, metric = 0;
		NMIP6Route *route;

		/* Address */
		if (!get_one_addr (model, &tree_iter, COL_ADDRESS, TRUE, &dest)) {
			g_warning ("%s: IPv6 address missing or invalid!", __func__);
			goto next;
		}

		/* Prefix */
		if (!get_one_int (model, &tree_iter, COL_PREFIX, 128, TRUE, &prefix)) {
			g_warning ("%s: IPv6 prefix missing or invalid!", __func__);
			goto next;
		}

		/* Next hop (optional) */
		memset (&next_hop, 0, sizeof (struct in6_addr));
		if (!get_one_addr (model, &tree_iter, COL_NEXT_HOP, FALSE, &next_hop)) {
			g_warning ("%s: IPv6 next hop invalid!", __func__);
			goto next;
		}

		/* Metric (optional) */
		if (!get_one_int (model, &tree_iter, COL_METRIC, G_MAXUINT32, FALSE, &metric)) {
			g_warning ("%s: IPv6 metric invalid!", __func__);
			goto next;
		}

		route = nm_ip6_route_new ();
		nm_ip6_route_set_dest (route, &dest);
		nm_ip6_route_set_prefix (route, prefix);
		nm_ip6_route_set_next_hop (route, &next_hop);
		nm_ip6_route_set_metric (route, metric);
		nm_setting_ip6_config_add_route (s_ip6, route);
		nm_ip6_route_unref (route);

	next:
		iter_valid = gtk_tree_model_iter_next (model, &tree_iter);
	}

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_ignore_auto_routes"));
	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_IGNORE_AUTO_ROUTES,
	              gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)),
	              NULL);

	widget = GTK_WIDGET (gtk_builder_get_object (builder, "ip6_never_default"));
	g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT,
	              gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)),
	              NULL);
}
static GPtrArray *
read_ip4_addresses (GKeyFile *file,
			    const char *setting_name,
			    const char *key)
{
	GPtrArray *addresses;
	int i = 0;

	addresses = g_ptr_array_sized_new (3);

	/* Look for individual addresses */
	while (i++ < 1000) {
		gchar **tmp, **iter;
		char *key_name;
		gsize length = 0;
		int ret;
		GArray *address;
		guint32 empty = 0;
		int j;

		key_name = g_strdup_printf ("%s%d", key, i);
		tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
		g_free (key_name);

		if (!tmp || !length)
			break; /* all done */

		if ((length < 2) || (length > 3)) {
			g_warning ("%s: ignoring invalid IPv4 address item '%s'", __func__, key_name);
			goto next;
		}

		/* convert the string array into IP addresses */
		address = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
		for (iter = tmp, j = 0; *iter; iter++, j++) {
			struct in_addr addr;

			if (j == 1) {
				guint32 prefix = 0;

				/* prefix */
				if (!get_one_int (*iter, 32, key_name, &prefix)) {
					g_array_free (address, TRUE);
					goto next;
				}

				g_array_append_val (address, prefix);
			} else {
				/* address and gateway */
				ret = inet_pton (AF_INET, *iter, &addr);
				if (ret <= 0) {
					g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter);
					g_array_free (address, TRUE);
					goto next;
				}
				g_array_append_val (address, addr.s_addr);
			}
		}

		/* fill in blank gateway if not specified */
		if (address->len == 2)
			g_array_append_val (address, empty);

		g_ptr_array_add (addresses, address);

next:
		g_strfreev (tmp);
	}

	if (addresses->len < 1) {
		g_ptr_array_free (addresses, TRUE);
		addresses = NULL;
	}

	return addresses;
}
static GPtrArray *
read_ip6_routes (GKeyFile *file,
                 const char *setting_name,
                 const char *key)
{
	GPtrArray *routes;
	struct in6_addr addr;
	guint32 prefix, metric;
	int i = 0;

	routes = g_ptr_array_sized_new (3);

	/* Look for individual routes */
	while (i++ < 1000) {
		gchar **tmp;
		char *key_name, *str_prefix;
		gsize length = 0;
		int ret;
		GValueArray *values;
		GByteArray *address;
		GValue value = { 0 };

		key_name = g_strdup_printf ("%s%d", key, i);
		tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
		g_free (key_name);

		if (!tmp || !length)
			break; /* all done */

		if (length != 3) {
			g_warning ("%s: ignoring invalid IPv6 address item '%s'", __func__, key_name);
			goto next;
		}

		/* convert the string array into IPv6 routes */
		values = g_value_array_new (4); /* NMIP6Route has 4 items */

		/* Split the route and prefix */
		str_prefix = split_prefix (tmp[0]);

		/* destination address */
		ret = inet_pton (AF_INET6, tmp[0], &addr);
		if (ret <= 0) {
			g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[0]);
			g_value_array_free (values);
			goto next;
		}
		address = g_byte_array_new ();
		g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
		g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
		g_value_take_boxed (&value, address);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		/* prefix */
		prefix = 0;
		if (str_prefix) {
			if (!get_one_int (str_prefix, 128, key_name, &prefix)) {
				g_value_array_free (values);
				goto next;
			}
		} else {
			/* default to 64 if unspecified */
			prefix = 64;
		}
		g_value_init (&value, G_TYPE_UINT);
		g_value_set_uint (&value, prefix);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		/* next hop address */
		ret = inet_pton (AF_INET6, tmp[1], &addr);
		if (ret <= 0) {
			g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp[1]);
			g_value_array_free (values);
			goto next;
		}
		address = g_byte_array_new ();
		g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
		g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
		g_value_take_boxed (&value, address);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		/* metric */
		metric = 0;
		if (!get_one_int (tmp[2], G_MAXUINT32, key_name, &metric)) {
			g_value_array_free (values);
			goto next;
		}
		g_value_init (&value, G_TYPE_UINT);
		g_value_set_uint (&value, metric);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		g_ptr_array_add (routes, values);

next:
		g_strfreev (tmp);
	}

	if (routes->len < 1) {
		g_ptr_array_free (routes, TRUE);
		routes = NULL;
	}

	return routes;
}
static GPtrArray *
read_ip6_addresses (GKeyFile *file,
                    const char *setting_name,
                    const char *key)
{
	GPtrArray *addresses;
	struct in6_addr addr, gw;
	guint32 prefix;
	int i = 0;

	addresses = g_ptr_array_sized_new (3);

	/* Look for individual addresses */
	while (i++ < 1000) {
		char *tmp, *key_name, *str_prefix, *str_gw;
		int ret;
		GValueArray *values;
		GByteArray *address;
		GByteArray *gateway;
		GValue value = { 0 };

		key_name = g_strdup_printf ("%s%d", key, i);
		tmp = g_key_file_get_string (file, setting_name, key_name, NULL);
		g_free (key_name);

		if (!tmp)
			break; /* all done */

		/* convert the string array into IPv6 addresses */
		values = g_value_array_new (2); /* NMIP6Address has 2 items */

		/* Split the address and prefix */
		str_prefix = split_prefix (tmp);

		/* address */
		ret = inet_pton (AF_INET6, tmp, &addr);
		if (ret <= 0) {
			g_warning ("%s: ignoring invalid IPv6 %s element '%s'", __func__, key_name, tmp);
			g_value_array_free (values);
			goto next;
		}

		address = g_byte_array_new ();
		g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
		g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
		g_value_take_boxed (&value, address);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		/* prefix */
		prefix = 0;
		if (str_prefix) {
			if (!get_one_int (str_prefix, 128, key_name, &prefix)) {
				g_value_array_free (values);
				goto next;
			}
		} else {
			/* Missing prefix defaults to /64 */
			prefix = 64;
		}

		g_value_init (&value, G_TYPE_UINT);
		g_value_set_uint (&value, prefix);
		g_value_array_append (values, &value);
		g_value_unset (&value);

		/* Gateway (optional) */
		str_gw = split_gw (str_prefix);
		if (str_gw) {
			ret = inet_pton (AF_INET6, str_gw, &gw);
			if (ret <= 0) {
				g_warning ("%s: ignoring invalid IPv6 %s gateway '%s'", __func__, key_name, tmp);
				g_value_array_free (values);
				goto next;
			}

			if (!IN6_IS_ADDR_UNSPECIFIED (&gw)) {
				gateway = g_byte_array_new ();
				g_byte_array_append (gateway, (guint8 *) gw.s6_addr, 16);
				g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
				g_value_take_boxed (&value, gateway);
				g_value_array_append (values, &value);
				g_value_unset (&value);
			}
		}

		g_ptr_array_add (addresses, values);

next:
		g_free (tmp);
	}

	if (addresses->len < 1) {
		g_ptr_array_free (addresses, TRUE);
		addresses = NULL;
	}

	return addresses;
}
static GPtrArray *
read_ip4_routes (GKeyFile *file,
			 const char *setting_name,
			 const char *key)
{
	GPtrArray *routes;
	int i = 0;

	routes = g_ptr_array_sized_new (3);

	/* Look for individual routes */
	while (i++ < 1000) {
		gchar **tmp, **iter;
		char *key_name;
		gsize length = 0;
		int ret;
		GArray *route;
		int j;

		key_name = g_strdup_printf ("%s%d", key, i);
		tmp = g_key_file_get_string_list (file, setting_name, key_name, &length, NULL);
		g_free (key_name);

		if (!tmp || !length)
			break; /* all done */

		if (length != 4) {
			g_warning ("%s: ignoring invalid IPv4 route item '%s'", __func__, key_name);
			goto next;
		}

		/* convert the string array into IP addresses */
		route = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 4);
		for (iter = tmp, j = 0; *iter; iter++, j++) {
			struct in_addr addr;

			if (j == 1) {
				guint32 prefix = 0;

				/* prefix */
				if (!get_one_int (*iter, 32, key_name, &prefix)) {
					g_array_free (route, TRUE);
					goto next;
				}

				g_array_append_val (route, prefix);
			} else if (j == 3) {
				guint32 metric = 0;

				/* metric */
				if (!get_one_int (*iter, G_MAXUINT32, key_name, &metric)) {
					g_array_free (route, TRUE);
					goto next;
				}

				g_array_append_val (route, metric);
			} else {
				/* address and next hop */
				ret = inet_pton (AF_INET, *iter, &addr);
				if (ret <= 0) {
					g_warning ("%s: ignoring invalid IPv4 %s element '%s'", __func__, key_name, *iter);
					g_array_free (route, TRUE);
					goto next;
				}
				g_array_append_val (route, addr.s_addr);
			}
		}
		g_ptr_array_add (routes, route);

next:
		g_strfreev (tmp);
	}

	if (routes->len < 1) {
		g_ptr_array_free (routes, TRUE);
		routes = NULL;
	}

	return routes;
}
Example #10
0
static gpointer
build_ip6_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route)
{
	GValueArray *result;
	struct in6_addr addr;
	GByteArray *address;
	GByteArray *gateway;
	guint32 metric = 0;
	GValue value = G_VALUE_INIT;
	int err;

	g_return_val_if_fail (address_str, NULL);

	result = g_value_array_new (3);

	/* add address */
	err = inet_pton (AF_INET6, address_str, &addr);
	if (err <= 0) {
		g_warning ("%s: ignoring invalid IPv6 address '%s'", __func__, address_str);
		goto error_out;
	}
	address = g_byte_array_new ();
	g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
	g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
	g_value_take_boxed (&value, address);
	g_value_array_append (result, &value);
	g_value_unset (&value);

	/* add prefix length */
	g_value_init (&value, G_TYPE_UINT);
	g_value_set_uint (&value, plen);
	g_value_array_append (result, &value);
	g_value_unset (&value);

	/* add gateway */
	if (gateway_str && gateway_str[0]) {
		err = inet_pton (AF_INET6, gateway_str, &addr);
		if (err <= 0) {
			/* Try workaround for routes written by broken keyfile writer.
			 * Due to bug bgo#719851, an older version of writer would have
			 * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
			 * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric"
			 * Try workaround by interepeting gateway_str as metric to accept such
			 * invalid routes. This broken syntax should not be not officially
			 * supported.
			 **/
			if (route && !metric_str && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric))
				addr = in6addr_any;
			else {
				g_warning ("%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str);
				goto error_out;
			}
		}
	} else
		addr = in6addr_any;

	/* parse metric, default to 0 */
	if (metric_str) {
		if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
			goto error_out;
	}

	gateway = g_byte_array_new ();
	g_byte_array_append (gateway, (guint8 *) addr.s6_addr, 16);
	g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
	g_value_take_boxed (&value, gateway);
	g_value_array_append (result, &value);
	g_value_unset (&value);

	/* add metric (for routing) */
	if (route) {
		g_value_init (&value, G_TYPE_UINT);
		g_value_set_uint (&value, metric);
		g_value_array_append (result, &value);
		g_value_unset (&value);
	}

	return result;

error_out:
	g_value_array_free (result);
	return NULL;
}