nm-ip6-config.c 88.4 KB
Newer Older
Dan Winship's avatar
Dan Winship committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
 *
 * 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.
 *
18
 * Copyright (C) 2005 - 2017 Red Hat, Inc.
Dan Winship's avatar
Dan Winship committed
19 20 21
 * Copyright (C) 2006 - 2008 Novell, Inc.
 */

22
#include "nm-default.h"
23

Dan Winship's avatar
Dan Winship committed
24
#include "nm-ip6-config.h"
25

26
#include <arpa/inet.h>
27
#include <resolv.h>
28
#include <linux/rtnetlink.h>
29

30
#include "nm-glib-aux/nm-dedup-multi.h"
31

Dan Winship's avatar
Dan Winship committed
32
#include "nm-utils.h"
33
#include "platform/nmp-object.h"
34 35
#include "platform/nm-platform.h"
#include "platform/nm-platform-utils.h"
36
#include "nm-core-internal.h"
37
#include "NetworkManagerUtils.h"
38
#include "nm-ip4-config.h"
39
#include "ndisc/nm-ndisc.h"
40
#include "nm-dbus-object.h"
41

42 43
/*****************************************************************************/

44 45 46 47 48 49 50 51 52 53 54 55 56 57
static gboolean
_route_valid (const NMPlatformIP6Route *r)
{
	struct in6_addr n;

	return    r
	       && r->plen <= 128
	       && (memcmp (&r->network,
	                   nm_utils_ip6_address_clear_host_address (&n, &r->network, r->plen),
	                   sizeof (n)) == 0);
}

/*****************************************************************************/

58
typedef struct {
59 60 61
	int ifindex;
	int dns_priority;
	NMSettingIP6ConfigPrivacy privacy;
Dan Winship's avatar
Dan Winship committed
62 63 64
	GArray *nameservers;
	GPtrArray *domains;
	GPtrArray *searches;
65
	GPtrArray *dns_options;
66 67
	GVariant *address_data_variant;
	GVariant *addresses_variant;
68 69
	GVariant *route_data_variant;
	GVariant *routes_variant;
70
	NMDedupMultiIndex *multi_idx;
71
	const NMPObject *best_default_route;
72 73 74 75
	union {
		NMIPConfigDedupMultiIdxType idx_ip6_addresses_;
		NMDedupMultiIdxType idx_ip6_addresses;
	};
76 77 78 79
	union {
		NMIPConfigDedupMultiIdxType idx_ip6_routes_;
		NMDedupMultiIdxType idx_ip6_routes;
	};
Dan Winship's avatar
Dan Winship committed
80 81
} NMIP6ConfigPrivate;

82
struct _NMIP6Config {
83
	NMDBusObject parent;
84 85 86 87
	NMIP6ConfigPrivate _priv;
};

struct _NMIP6ConfigClass {
88
	NMDBusObjectClass parent;
89 90
};

91
G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_DBUS_OBJECT)
92

93
#define NM_IP6_CONFIG_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMIP6Config, NM_IS_IP6_CONFIG)
Dan Winship's avatar
Dan Winship committed
94

95
NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config,
96
	PROP_MULTI_IDX,
97
	PROP_IFINDEX,
98
	PROP_ADDRESS_DATA,
Dan Winship's avatar
Dan Winship committed
99
	PROP_ADDRESSES,
100
	PROP_ROUTE_DATA,
101
	PROP_ROUTES,
102
	PROP_GATEWAY,
Dan Winship's avatar
Dan Winship committed
103 104
	PROP_NAMESERVERS,
	PROP_DOMAINS,
105
	PROP_SEARCHES,
106
	PROP_DNS_OPTIONS,
107
	PROP_DNS_PRIORITY,
108
);
Dan Winship's avatar
Dan Winship committed
109

110
/*****************************************************************************/
111

112
static void _add_address (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Address *new);
113
static void _add_route (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Route *new, const NMPObject **out_obj_new);
114 115 116
static const NMDedupMultiEntry *_lookup_route (const NMIP6Config *self,
                                               const NMPObject *needle,
                                               NMPlatformIPRouteCmpType cmp_type);
117 118 119

/*****************************************************************************/

120
int
121
nm_ip6_config_get_ifindex (const NMIP6Config *self)
122
{
123
	return NM_IP6_CONFIG_GET_PRIVATE (self)->ifindex;
124 125
}

126
NMDedupMultiIndex *
127
nm_ip6_config_get_multi_idx (const NMIP6Config *self)
128
{
129
	return NM_IP6_CONFIG_GET_PRIVATE (self)->multi_idx;
130 131
}

132
void
133
nm_ip6_config_set_privacy (NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy)
134
{
135
	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
136 137 138 139

	priv->privacy = privacy;
}

140
/*****************************************************************************/
141

142 143
const NMDedupMultiHeadEntry *
nm_ip6_config_lookup_addresses (const NMIP6Config *self)
144 145 146 147
{
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);

	return nm_dedup_multi_index_lookup_head (priv->multi_idx,
148
	                                         &priv->idx_ip6_addresses,
149 150 151 152
	                                         NULL);
}

void
153
nm_ip_config_iter_ip6_address_init (NMDedupMultiIter *ipconf_iter, const NMIP6Config *self)
154 155
{
	g_return_if_fail (NM_IS_IP6_CONFIG (self));
156
	nm_dedup_multi_iter_init (ipconf_iter, nm_ip6_config_lookup_addresses (self));
157 158
}

159 160 161 162
/*****************************************************************************/

const NMDedupMultiHeadEntry *
nm_ip6_config_lookup_routes (const NMIP6Config *self)
163
{
164
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
165

166 167 168 169 170 171 172 173 174 175
	return nm_dedup_multi_index_lookup_head (priv->multi_idx,
	                                         &priv->idx_ip6_routes,
	                                         NULL);
}

void
nm_ip_config_iter_ip6_route_init (NMDedupMultiIter *ipconf_iter, const NMIP6Config *self)
{
	g_return_if_fail (NM_IS_IP6_CONFIG (self));
	nm_dedup_multi_iter_init (ipconf_iter, nm_ip6_config_lookup_routes (self));
176 177 178 179
}

/*****************************************************************************/

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
const NMPObject *
nm_ip6_config_best_default_route_get (const NMIP6Config *self)
{
	g_return_val_if_fail (NM_IS_IP6_CONFIG (self), NULL);

	return NM_IP6_CONFIG_GET_PRIVATE (self)->best_default_route;
}

const NMPObject *
_nm_ip6_config_best_default_route_find (const NMIP6Config *self)
{
	NMDedupMultiIter ipconf_iter;
	const NMPObject *new_best_default_route = NULL;

	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, NULL) {
		new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route,
		                                                                       ipconf_iter.current->obj);
	}
	return new_best_default_route;
}

/*****************************************************************************/

203
static void
204
_notify_addresses (NMIP6Config *self)
205 206 207 208 209
{
	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);

	nm_clear_g_variant (&priv->address_data_variant);
	nm_clear_g_variant (&priv->addresses_variant);
210 211
	nm_gobject_notify_together (self, PROP_ADDRESS_DATA,
	                                  PROP_ADDRESSES);
212 213
}

214 215 216
static void
_notify_routes (NMIP6Config *self)
{
217 218
	NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);

219
	nm_assert (priv->best_default_route == _nm_ip6_config_best_default_route_find (self));
220 221
	nm_clear_g_variant (&priv->route_data_variant);
	nm_clear_g_variant (&priv->routes_variant);
222 223
	nm_gobject_notify_together (self, PROP_ROUTE_DATA,
	                                  PROP_ROUTES);
224 225 226 227
}

/*****************************************************************************/

228
static int
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
_addresses_sort_cmp_get_prio (const struct in6_addr *addr)
{
	if (IN6_IS_ADDR_V4MAPPED (addr))
		return 0;
	if (IN6_IS_ADDR_V4COMPAT (addr))
		return 1;
	if (IN6_IS_ADDR_UNSPECIFIED (addr))
		return 2;
	if (IN6_IS_ADDR_LOOPBACK (addr))
		return 3;
	if (IN6_IS_ADDR_LINKLOCAL (addr))
		return 4;
	if (IN6_IS_ADDR_SITELOCAL (addr))
		return 5;
	return 6;
}

246 247 248 249
static int
_addresses_sort_cmp (const NMPlatformIP6Address *a1,
                     const NMPlatformIP6Address *a2,
                     gboolean prefer_temp)
250
{
251
	int p1, p2, c;
252 253 254 255 256
	gboolean perm1, perm2, tent1, tent2;
	gboolean ipv6_privacy1, ipv6_privacy2;

	/* tentative addresses are always sorted back... */
	/* sort tentative addresses after non-tentative. */
257 258
	tent1 = (a1->n_ifa_flags & IFA_F_TENTATIVE);
	tent2 = (a2->n_ifa_flags & IFA_F_TENTATIVE);
259 260 261 262 263 264 265 266 267 268
	if (tent1 != tent2)
		return tent1 ? 1 : -1;

	/* Sort by address type. For example link local will
	 * be sorted *after* site local or global. */
	p1 = _addresses_sort_cmp_get_prio (&a1->address);
	p2 = _addresses_sort_cmp_get_prio (&a2->address);
	if (p1 != p2)
		return p1 > p2 ? -1 : 1;

269 270
	ipv6_privacy1 = !!(a1->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
	ipv6_privacy2 = !!(a2->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
271 272 273 274
	if (ipv6_privacy1 || ipv6_privacy2) {
		gboolean public1 = TRUE, public2 = TRUE;

		if (ipv6_privacy1) {
275
			if (a1->n_ifa_flags & IFA_F_TEMPORARY)
276 277 278 279 280
				public1 = prefer_temp;
			else
				public1 = !prefer_temp;
		}
		if (ipv6_privacy2) {
281
			if (a2->n_ifa_flags & IFA_F_TEMPORARY)
282 283 284 285 286 287 288 289 290 291
				public2 = prefer_temp;
			else
				public2 = !prefer_temp;
		}

		if (public1 != public2)
			return public1 ? -1 : 1;
	}

	/* Sort the addresses based on their source. */
292 293
	if (a1->addr_source != a2->addr_source)
		return a1->addr_source > a2->addr_source ? -1 : 1;
294 295

	/* sort permanent addresses before non-permanent. */
296 297
	perm1 = (a1->n_ifa_flags & IFA_F_PERMANENT);
	perm2 = (a2->n_ifa_flags & IFA_F_PERMANENT);
298 299 300 301 302 303 304 305
	if (perm1 != perm2)
		return perm1 ? -1 : 1;

	/* finally sort addresses lexically */
	c = memcmp (&a1->address, &a2->address, sizeof (a2->address));
	return c != 0 ? c : memcmp (a1, a2, sizeof (*a1));
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
static int
_addresses_sort_cmp_prop (gconstpointer a, gconstpointer b, gpointer user_data)
{
	return _addresses_sort_cmp (NMP_OBJECT_CAST_IP6_ADDRESS (*((const NMPObject **) a)),
	                            NMP_OBJECT_CAST_IP6_ADDRESS (*((const NMPObject **) b)),
	                            ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
}

static int
sort_captured_addresses (const CList *lst_a, const CList *lst_b, gconstpointer user_data)
{
	const NMPlatformIP6Address *addr_a = NMP_OBJECT_CAST_IP6_ADDRESS (c_list_entry (lst_a, NMDedupMultiEntry, lst_entries)->obj);
	const NMPlatformIP6Address *addr_b = NMP_OBJECT_CAST_IP6_ADDRESS (c_list_entry (lst_b, NMDedupMultiEntry, lst_entries)->obj);

	return _addresses_sort_cmp (addr_a, addr_b,
	                            ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR);
}

324
gboolean
325
_nmtst_ip6_config_addresses_sort (NMIP6Config *self)
326 327
{
	NMIP6ConfigPrivate *priv;
328
	const NMDedupMultiHeadEntry *head_entry;
329 330 331

	g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE);

332 333 334 335 336 337 338 339 340 341 342 343
	head_entry = nm_ip6_config_lookup_addresses (self);
	if (head_entry && head_entry->len > 1) {
		gboolean changed;
		gs_free gconstpointer *addresses_old = NULL;
		guint naddr, j;
		NMDedupMultiIter iter;

		priv = NM_IP6_CONFIG_GET_PRIVATE (self);

		addresses_old = nm_dedup_multi_objs_to_array_head (head_entry, NULL, NULL, &naddr);
		nm_assert (addresses_old);
		nm_assert (naddr > 0 && naddr == head_entry->len);
344

345 346 347
		nm_dedup_multi_head_entry_sort (head_entry,
		                                sort_captured_addresses,
		                                GINT_TO_POINTER (priv->privacy));
348

349 350 351 352 353 354 355 356
		changed = FALSE;
		j = 0;
		nm_dedup_multi_iter_for_each (&iter, head_entry) {
			nm_assert (j < naddr);
			if (iter.current->obj != addresses_old[j++])
				changed = TRUE;
		}
		nm_assert (j == naddr);
357 358

		if (changed) {
359
			_notify_addresses (self);
360 361 362 363 364 365
			return TRUE;
		}
	}
	return FALSE;
}

366 367 368 369 370 371 372 373 374 375 376
NMIP6Config *
nm_ip6_config_clone (const NMIP6Config *self)
{
	NMIP6Config *copy;

	copy = nm_ip6_config_new (nm_ip6_config_get_multi_idx (self), -1);
	nm_ip6_config_replace (copy, self, NULL);

	return copy;
}

377
NMIP6Config *
378
nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, NMSettingIP6ConfigPrivacy use_temporary)
379
{
380
	NMIP6Config *self;
381
	NMIP6ConfigPrivate *priv;
382
	const NMDedupMultiHeadEntry *head_entry;
383 384
	NMDedupMultiIter iter;
	const NMPObject *plobj = NULL;
385 386

	nm_assert (ifindex > 0);
387 388

	/* Slaves have no IP configuration */
389
	if (nm_platform_link_get_master (platform, ifindex) > 0)
390 391
		return NULL;

392 393
	self = nm_ip6_config_new (multi_idx, ifindex);
	priv = NM_IP6_CONFIG_GET_PRIVATE (self);
394

395 396 397
	head_entry = nm_platform_lookup_object (platform,
	                                        NMP_OBJECT_TYPE_IP6_ADDRESS,
	                                        ifindex);
398 399
	if (head_entry) {
		nmp_cache_iter_for_each (&iter, head_entry, &plobj) {
400 401 402 403 404 405
			if (!_nm_ip_config_add_obj (priv->multi_idx,
			                            &priv->idx_ip6_addresses_,
			                            ifindex,
			                            plobj,
			                            NULL,
			                            FALSE,
406
			                            TRUE,
407
			                            NULL,
408
			                            NULL))
409 410 411 412 413 414 415
				nm_assert_not_reached ();
		}
		head_entry = nm_ip6_config_lookup_addresses (self);
		nm_assert (head_entry);
		nm_dedup_multi_head_entry_sort (head_entry,
		                                sort_captured_addresses,
		                                GINT_TO_POINTER (use_temporary));
416
		_notify_addresses (self);
417
	}
418

419 420 421
	head_entry = nm_platform_lookup_object (platform,
	                                        NMP_OBJECT_TYPE_IP6_ROUTE,
	                                        ifindex);
422

423
	nmp_cache_iter_for_each (&iter, head_entry, &plobj)
424
		_add_route (self, plobj, NULL, NULL);
425

426
	return self;
427 428
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
void
nm_ip6_config_update_routes_metric (NMIP6Config *self, gint64 metric)
{
	gs_free NMPlatformIP6Route *routes = NULL;
	gboolean need_update = FALSE;
	const NMPlatformIP6Route *r;
	NMDedupMultiIter iter;
	guint num = 0, i = 0;

	nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) {
		if (r->metric != metric)
			need_update = TRUE;
		num++;
	}
	if (!need_update)
		return;

	routes = g_new (NMPlatformIP6Route, num);
	nm_ip_config_iter_ip6_route_for_each (&iter, self, &r) {
		routes[i] = *r;
		routes[i].metric = metric;
		i++;
	}

	g_object_freeze_notify (G_OBJECT (self));
	nm_ip6_config_reset_routes (self);
	for (i = 0; i < num; i++)
		nm_ip6_config_add_route (self, &routes[i], NULL);
	g_object_thaw_notify (G_OBJECT (self));
}

460
void
461 462 463
nm_ip6_config_add_dependent_routes (NMIP6Config *self,
                                    guint32 route_table,
                                    guint32 route_metric)
464
{
465 466
	const NMPlatformIP6Address *my_addr;
	const NMPlatformIP6Route *my_route;
467 468 469 470 471 472 473 474 475 476 477 478 479
	int ifindex;
	NMDedupMultiIter iter;

	g_return_if_fail (NM_IS_IP6_CONFIG (self));

	ifindex = nm_ip6_config_get_ifindex (self);
	g_return_if_fail (ifindex > 0);

	/* For IPv6 addresses received via SLAAC/autoconf, we explicitly add the
	 * device-routes (onlink) to NMIP6Config.
	 *
	 * For manually added IPv6 routes, add the device routes explicitly. */

480
	nm_ip_config_iter_ip6_address_for_each (&iter, self, &my_addr) {
481 482 483 484
		NMPlatformIP6Route *route;
		gboolean has_peer;
		int routes_n, routes_i;

485
		if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE))
486
			continue;
487 488
		if (my_addr->plen == 0)
			continue;
489

490
		has_peer = !IN6_IS_ADDR_UNSPECIFIED (&my_addr->peer_address);
491 492 493 494

		/* If we have an IPv6 peer, we add two /128 routes
		 * (unless, both addresses are identical). */
		routes_n = (   has_peer
495
		            && !IN6_ARE_ADDR_EQUAL (&my_addr->address, &my_addr->peer_address))
496 497 498
		           ? 2 : 1;

		for (routes_i = 0; routes_i < routes_n; routes_i++) {
499
			nm_auto_nmpobj NMPObject *r = NULL;
500 501 502 503 504 505

			r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
			route = NMP_OBJECT_CAST_IP6_ROUTE (r);

			route->ifindex = ifindex;
			route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL;
506
			route->table_coerced = nm_platform_route_table_coerce (route_table);
507
			route->metric = route_metric;
508 509 510

			if (has_peer) {
				if (routes_i == 0)
511
					route->network = my_addr->address;
512
				else
513
					route->network = my_addr->peer_address;
514 515
				route->plen = 128;
			} else {
516 517
				nm_utils_ip6_address_clear_host_address (&route->network, &my_addr->address, my_addr->plen);
				route->plen = my_addr->plen;
518 519 520 521 522 523 524 525 526 527 528 529
			}

			nm_platform_ip_route_normalize (AF_INET6, (NMPlatformIPRoute *) route);

			if (_lookup_route (self,
			                   r,
			                   NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) {
				/* we already track this route. Don't add it again. */
			} else
				_add_route (self, r, NULL, NULL);
		}
	}
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550

again:
	nm_ip_config_iter_ip6_route_for_each (&iter, self, &my_route) {
		NMPlatformIP6Route rt;

		if (   !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (my_route)
		    || IN6_IS_ADDR_UNSPECIFIED (&my_route->gateway)
		    || NM_IS_IP_CONFIG_SOURCE_RTPROT (my_route->rt_source)
		    || nm_ip6_config_get_direct_route_for_host (self,
		                                                &my_route->gateway,
		                                                nm_platform_route_table_uncoerce (my_route->table_coerced, TRUE)))
			continue;

		rt = *my_route;
		rt.network = my_route->gateway;
		rt.plen = 128;
		rt.gateway = in6addr_any;
		_add_route (self, NULL, &rt, NULL);
		/* adding the route might have invalidated the iteration. Start again. */
		goto again;
	}
551 552
}

553
gboolean
554
nm_ip6_config_commit (const NMIP6Config *self,
555
                      NMPlatform *platform,
556
                      NMIPRouteTableSyncMode route_table_sync,
557
                      GPtrArray **out_temporary_not_available)
558
{
559
	gs_unref_ptrarray GPtrArray *addresses = NULL;
560
	gs_unref_ptrarray GPtrArray *routes = NULL;
561
	gs_unref_ptrarray GPtrArray *routes_prune = NULL;
562 563 564 565
	int ifindex;
	gboolean success = TRUE;

	g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE);
566

567
	ifindex = nm_ip6_config_get_ifindex (self);
568 569
	g_return_val_if_fail (ifindex > 0, FALSE);

570 571
	addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_addresses (self),
	                                                   NULL, NULL);
572

573 574
	routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_routes (self),
	                                                NULL, NULL);
575 576 577

	routes_prune = nm_platform_ip_route_get_prune_list (platform,
	                                                    AF_INET6,
578 579
	                                                    ifindex,
	                                                    route_table_sync);
580

581
	nm_platform_ip6_address_sync (platform, ifindex, addresses, FALSE);
582

583 584 585 586
	if (!nm_platform_ip_route_sync (platform,
	                                AF_INET6,
	                                ifindex,
	                                routes,
587
	                                routes_prune,
588
	                                out_temporary_not_available))
589
		success = FALSE;
590

591
	return success;
592 593
}

Dan Winship's avatar
Dan Winship committed
594
void
595 596 597 598
nm_ip6_config_merge_setting (NMIP6Config *self,
                             NMSettingIPConfig *setting,
                             guint32 route_table,
                             guint32 route_metric)
Dan Winship's avatar
Dan Winship committed
599
{
600
	guint naddresses, nroutes, nnameservers, nsearches;
601
	const char *gateway_str;
602
	struct in6_addr gateway_bin;
603
	int i, priority;
604

605 606 607
	if (!setting)
		return;

608 609 610 611 612 613
	g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));

	naddresses = nm_setting_ip_config_get_num_addresses (setting);
	nroutes = nm_setting_ip_config_get_num_routes (setting);
	nnameservers = nm_setting_ip_config_get_num_dns (setting);
	nsearches = nm_setting_ip_config_get_num_dns_searches (setting);
614

615
	g_object_freeze_notify (G_OBJECT (self));
616

617
	/* Gateway */
618 619 620 621 622 623 624 625 626 627
	if (   !nm_setting_ip_config_get_never_default (setting)
	    && (gateway_str = nm_setting_ip_config_get_gateway (setting))
	    && inet_pton (AF_INET6, gateway_str, &gateway_bin) == 1
	    && !IN6_IS_ADDR_UNSPECIFIED (&gateway_bin)) {
		const NMPlatformIP6Route r = {
			.rt_source = NM_IP_CONFIG_SOURCE_USER,
			.gateway = gateway_bin,
			.table_coerced = nm_platform_route_table_coerce (route_table),
			.metric = route_metric,
		};
628

629
		_add_route (self, NULL, &r, NULL);
630 631
	}

632
	/* Addresses */
633
	for (i = 0; i < naddresses; i++) {
634
		NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
635 636 637
		NMPlatformIP6Address address;

		memset (&address, 0, sizeof (address));
638 639
		nm_ip_address_get_address_binary (s_addr, &address.address);
		address.plen = nm_ip_address_get_prefix (s_addr);
640
		nm_assert (address.plen <= 128);
641 642
		address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
		address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
643
		address.addr_source = NM_IP_CONFIG_SOURCE_USER;
644

645
		_add_address (self, NULL, &address);
646
	}
647

648
	/* Routes */
649
	for (i = 0; i < nroutes; i++) {
650
		NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
651 652
		NMPlatformIP6Route route;

653 654 655 656 657
		if (nm_ip_route_get_family (s_route) != AF_INET6) {
			nm_assert_not_reached ();
			continue;
		}

658
		memset (&route, 0, sizeof (route));
659
		nm_ip_route_get_dest_binary (s_route, &route.network);
660

661
		route.plen = nm_ip_route_get_prefix (s_route);
662 663 664 665
		nm_assert (route.plen <= 128);
		if (route.plen == 0)
			continue;

666
		nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
667
		if (nm_ip_route_get_metric (s_route) == -1)
668
			route.metric = route_metric;
669 670
		else
			route.metric = nm_ip_route_get_metric (s_route);
671
		route.rt_source = NM_IP_CONFIG_SOURCE_USER;
672

673 674
		nm_utils_ip6_address_clear_host_address (&route.network, &route.network, route.plen);

675 676 677 678
		_nm_ip_config_merge_route_attributes (AF_INET,
		                                      s_route,
		                                      NM_PLATFORM_IP_ROUTE_CAST (&route),
		                                      route_table);
679
		_add_route (self, NULL, &route, NULL);
680
	}
681

682
	/* DNS */
683
	if (nm_setting_ip_config_get_ignore_auto_dns (setting)) {
684 685 686
		nm_ip6_config_reset_nameservers (self);
		nm_ip6_config_reset_domains (self);
		nm_ip6_config_reset_searches (self);
687
	}
688 689 690
	for (i = 0; i < nnameservers; i++) {
		 struct in6_addr ip;

691
		if (inet_pton (AF_INET6, nm_setting_ip_config_get_dns (setting, i), &ip) == 1)
692
			nm_ip6_config_add_nameserver (self, &ip);
693
	}
694
	for (i = 0; i < nsearches; i++)
695
		nm_ip6_config_add_search (self, nm_setting_ip_config_get_dns_search (setting, i));
696

697 698
	i = 0;
	while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) {
699
		nm_ip6_config_add_dns_option (self, nm_setting_ip_config_get_dns_option (setting, i));
700 701 702
		i++;
	}

703 704
	priority = nm_setting_ip_config_get_dns_priority (setting);
	if (priority)
705
		nm_ip6_config_set_dns_priority (self, priority);
706

707
	g_object_thaw_notify (G_OBJECT (self));
Dan Winship's avatar
Dan Winship committed
708 709
}

710
NMSetting *
711
nm_ip6_config_create_setting (const NMIP6Config *self)
712
{
713
	const NMIP6ConfigPrivate *priv;
714
	NMSettingIPConfig *s_ip6;
715
	guint nnameservers, nsearches, noptions;
716
	const char *method = NULL;
717
	char sbuf[NM_UTILS_INET_ADDRSTRLEN];
718
	int i;
719
	NMDedupMultiIter ipconf_iter;
720
	const NMPlatformIP6Address *address;
721
	const NMPlatformIP6Route *route;
722

723
	s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
724

725
	if (!self) {
726
		g_object_set (s_ip6,
727
		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
728 729 730
		              NULL);
		return NM_SETTING (s_ip6);
	}
731

732 733
	priv = NM_IP6_CONFIG_GET_PRIVATE (self);

734 735 736
	nnameservers = nm_ip6_config_get_num_nameservers (self);
	nsearches = nm_ip6_config_get_num_searches (self);
	noptions = nm_ip6_config_get_num_dns_options (self);
737 738

	/* Addresses */
739
	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, self, &address) {
740
		NMIPAddress *s_addr;
741 742

		/* Ignore link-local address. */
743 744 745
		if (IN6_IS_ADDR_LINKLOCAL (&address->address)) {
			if (!method)
				method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
746
			continue;
747
		}
748

749
		/* Detect dynamic address */
750
		if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
751 752 753 754
			method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
			continue;
		}

755
		/* Static address found. */
756
		if (!method || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
757 758
			method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;

759
		s_addr = nm_ip_address_new_binary (AF_INET6, &address->address, address->plen, NULL);
760
		nm_setting_ip_config_add_address (s_ip6, s_addr);
761
		nm_ip_address_unref (s_addr);
762
	}
763

764
	/* Gateway */
765
	if (   priv->best_default_route
766
	    && nm_setting_ip_config_get_num_addresses (s_ip6) > 0) {
767
		g_object_set (s_ip6,
768 769
		              NM_SETTING_IP_CONFIG_GATEWAY,
		              nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (priv->best_default_route)->gateway,
770
		                                   sbuf),
771 772 773
		              NULL);
	}

774 775
	/* Use 'ignore' if the method wasn't previously set */
	if (!method)
776
		method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
777 778 779 780

	g_object_set (s_ip6,
	              NM_SETTING_IP_CONFIG_METHOD, method,
	              NULL);
781 782

	/* Routes */
783
	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, self, &route) {
784
		NMIPRoute *s_route;
785 786

		/* Ignore link-local route. */
787
		if (IN6_IS_ADDR_LINKLOCAL (&route->network))
788 789
			continue;

790
		if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
791 792
			continue;

793
		/* Ignore routes provided by external sources */
794
		if (route->rt_source != nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER))
795 796
			continue;

797 798 799 800
		s_route = nm_ip_route_new_binary (AF_INET6,
		                                  &route->network, route->plen,
		                                  &route->gateway, route->metric,
		                                  NULL);
801
		nm_setting_ip_config_add_route (s_ip6, s_route);
802
		nm_ip_route_unref (s_route);
803 804 805 806
	}

	/* DNS */
	for (i = 0; i < nnameservers; i++) {
807
		const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (self, i);
808

809
		nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, sbuf));
810 811
	}
	for (i = 0; i < nsearches; i++) {
812
		const char *search = nm_ip6_config_get_search (self, i);
813

814
		nm_setting_ip_config_add_dns_search (s_ip6, search);
815
	}
816
	for (i = 0; i < noptions; i++) {
817
		const char *option = nm_ip6_config_get_dns_option (self, i);
818 819 820 821

		nm_setting_ip_config_add_dns_option (s_ip6, option);
	}

822 823
	g_object_set (s_ip6,
	              NM_SETTING_IP_CONFIG_DNS_PRIORITY,
824
	              nm_ip6_config_get_dns_priority (self),
825
	              NULL);
826 827

	return NM_SETTING (s_ip6);
828 829
}

830
/*****************************************************************************/
831

832
void
833 834 835 836
nm_ip6_config_merge (NMIP6Config *dst,
                     const NMIP6Config *src,
                     NMIPConfigMergeFlags merge_flags,
                     guint32 default_route_metric_penalty)
837 838
{
	guint32 i;
839
	NMDedupMultiIter ipconf_iter;
840
	const NMPlatformIP6Address *address = NULL;
841 842 843 844

	g_return_if_fail (src != NULL);
	g_return_if_fail (dst != NULL);

845 846
	g_object_freeze_notify (G_OBJECT (dst));

847
	/* addresses */
848 849
	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &address)
		_add_address (dst, NMP_OBJECT_UP_CAST (address), NULL);
850 851

	/* nameservers */
852 853 854 855
	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
		for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
			nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
	}
856 857

	/* routes */
858
	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
859 860 861 862 863 864 865 866 867 868 869 870 871 872
		const NMPlatformIP6Route *r_src;

		nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r_src) {
			if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r_src)) {
				if (NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DEFAULT_ROUTES))
					continue;
				if (default_route_metric_penalty) {
					NMPlatformIP6Route r = *r_src;

					r.metric = nm_utils_ip_route_metric_penalize (AF_INET6, r.metric, default_route_metric_penalty);
					_add_route (dst, NULL, &r, NULL);
					continue;
				}
			}
873
			_add_route (dst, ipconf_iter.current->obj, NULL, NULL);
874
		}
875
	}
876 877

	/* domains */
878 879 880 881
	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
		for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
			nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
	}
882 883

	/* dns searches */
884 885 886 887
	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
		for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
			nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
	}
888

889
	/* dns options */
890 891 892 893
	if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
		for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++)
			nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
	}
894

895 896 897 898
	/* DNS priority */
	if (nm_ip6_config_get_dns_priority (src))
		nm_ip6_config_set_dns_priority (dst, nm_ip6_config_get_dns_priority (src));

899
	g_object_thaw_notify (G_OBJECT (dst));
900 901
}

902
/*****************************************************************************/
903 904 905 906

static int
_nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns)
{
907
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
908 909 910 911 912 913 914 915 916 917 918 919 920 921
	guint i;

	for (i = 0; i < priv->nameservers->len; i++) {
		const struct in6_addr *n = &g_array_index (priv->nameservers, struct in6_addr, i);

		if (IN6_ARE_ADDR_EQUAL (ns, n))
			return (int) i;
	}
	return -1;
}

static int
_domains_get_index (const NMIP6Config *self, const char *domain)
{
922
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
923 924 925 926 927 928 929 930 931 932 933 934 935 936
	guint i;

	for (i = 0; i < priv->domains->len; i++) {
		const char *d = g_ptr_array_index (priv->domains, i);

		if (g_strcmp0 (domain, d) == 0)
			return (int) i;
	}
	return -1;
}

static int
_searches_get_index (const NMIP6Config *self, const char *search)
{
937
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
938 939 940 941 942 943 944 945 946 947 948
	guint i;

	for (i = 0; i < priv->searches->len; i++) {
		const char *s = g_ptr_array_index (priv->searches, i);

		if (g_strcmp0 (search, s) == 0)
			return (int) i;
	}
	return -1;
}

949 950 951
static int
_dns_options_get_index (const NMIP6Config *self, const char *option)
{
952
	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
953 954 955 956 957 958 959 960 961 962 963
	guint i;

	for (i = 0; i < priv->dns_options->len; i++) {
		const char *s = g_ptr_array_index (priv->dns_options, i);

		if (g_strcmp0 (option, s) == 0)
			return (int) i;
	}
	return -1;
}

964
/*****************************************************************************/
965

966
/**
967
 * nm_ip6_config_subtract:
968 969
 * @dst: config from which to remove everything in @src
 * @src: config to remove from @dst
970 971 972 973 974
  * @default_route_metric_penalty: pretend that on source we applied
 *   a route penalty on the default-route. It means, for default routes
 *   we don't remove routes that match exactly, but those with a lower
 *   metric (with the penalty removed).
*
975 976 977
 * Removes everything in @src from @dst.
 */
void
978 979 980
nm_ip6_config_subtract (NMIP6Config *dst,
                        const NMIP6Config *src,
                        guint32 default_route_metric_penalty)
981
{
982
	NMIP6ConfigPrivate *dst_priv;
983
	guint i;
984
	int idx;
985
	const NMPlatformIP6Address *a;
986 987
	const NMPlatformIP6Route *r;
	NMDedupMultiIter ipconf_iter;
988
	gboolean changed;
989
	gboolean changed_default_route;
990 991 992 993

	g_return_if_fail (src != NULL);
	g_return_if_fail (dst != NULL);

994
	dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
995

996 997
	g_object_freeze_notify (G_OBJECT (dst));

998
	/* addresses */
999 1000
	changed = FALSE;
	nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &a) {
1001 1002
		if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx,
		                                     &dst_priv->idx_ip6_addresses,
1003 1004
		                                     NMP_OBJECT_UP_CAST (a),
		                                     NULL))
1005
			changed = TRUE;
1006
	}
1007 1008
	if (changed)
		_notify_addresses (dst);
1009 1010 1011

	/* nameservers */
	for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) {
1012 1013 1014
		idx = _nameservers_get_index (dst, nm_ip6_config_get_nameserver (src, i));
		if (idx >= 0)
			nm_ip6_config_del_nameserver (dst, idx);
1015 1016 1017
	}

	/* routes */
1018
	changed = FALSE;
1019
	changed_default_route = FALSE;
1020
	nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) {
1021 1022 1023
		const NMPObject *o_src = NMP_OBJECT_UP_CAST (r);
		NMPObject o_lookup_copy;
		const NMPObject *o_lookup;
1024 1025
		nm_auto_nmpobj const NMPObject *obj_old = NULL;

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
		if (   NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)
		    && default_route_metric_penalty) {
			NMPlatformIP6Route *rr;

			/* the default route was penalized when merging it to the combined ip-config.
			 * When subtracting the routes, we must re-do that process when comparing
			 * the routes. */
			o_lookup = nmp_object_stackinit_obj (&o_lookup_copy, o_src);
			rr = NMP_OBJECT_CAST_IP6_ROUTE (&o_lookup_copy);
			rr->metric = nm_utils_ip_route_metric_penalize (AF_INET6, rr->metric, default_route_metric_penalty);
		} else
			o_lookup = o_src;

1039 1040
		if (nm_dedup_multi_index_remove_obj (dst_priv->multi_idx,
		                                     &dst_priv->idx_ip6_routes,
1041
		                                     o_lookup,
1042 1043 1044 1045 1046
		                                     (gconstpointer *) &obj_old)) {
			if (dst_priv->best_default_route == obj_old) {
				nm_clear_nmp_object (&dst_priv->best_default_route);
				changed_default_route = TRUE;
			}
1047
			changed = TRUE;
1048 1049 1050 1051 1052
		}
	}
	if (changed_default_route) {
		_nm_ip_config_best_default_route_set (&dst_priv->best_default_route,
		                                      _nm_ip6_config_best_default_route_find (dst));
1053
		_notify (dst, PROP_GATEWAY);
1054
	}
1055 1056
	if (changed)
		_notify_routes (dst);
1057 1058 1059

	/* domains */
	for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) {