device: renew dhcp leases on awake for software devices
[NetworkManager.git] / src / nm-ip6-config.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Copyright (C) 2005 - 2013 Red Hat, Inc.
19  * Copyright (C) 2006 - 2008 Novell, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-ip6-config.h"
25
26 #include <string.h>
27 #include <arpa/inet.h>
28
29 #include "nm-utils.h"
30 #include "nm-platform.h"
31 #include "nm-route-manager.h"
32 #include "nm-core-internal.h"
33 #include "NetworkManagerUtils.h"
34
35 #include "nmdbus-ip6-config.h"
36
37 G_DEFINE_TYPE (NMIP6Config, nm_ip6_config, NM_TYPE_EXPORTED_OBJECT)
38
39 #define NM_IP6_CONFIG_GET_PRIVATE(o) ((o)->priv)
40
41 typedef struct _NMIP6ConfigPrivate {
42         gboolean never_default;
43         struct in6_addr gateway;
44         GArray *addresses;
45         GArray *routes;
46         GArray *nameservers;
47         GPtrArray *domains;
48         GPtrArray *searches;
49         GPtrArray *dns_options;
50         guint32 mss;
51         int ifindex;
52         gint64 route_metric;
53 } NMIP6ConfigPrivate;
54
55
56 NM_GOBJECT_PROPERTIES_DEFINE (NMIP6Config,
57         PROP_IFINDEX,
58         PROP_ADDRESS_DATA,
59         PROP_ADDRESSES,
60         PROP_ROUTE_DATA,
61         PROP_ROUTES,
62         PROP_GATEWAY,
63         PROP_NAMESERVERS,
64         PROP_DOMAINS,
65         PROP_SEARCHES,
66         PROP_DNS_OPTIONS,
67 );
68
69 NMIP6Config *
70 nm_ip6_config_new (int ifindex)
71 {
72         g_return_val_if_fail (ifindex >= -1, NULL);
73         return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG,
74                                              NM_IP6_CONFIG_IFINDEX, ifindex,
75                                              NULL);
76 }
77
78 NMIP6Config *
79 nm_ip6_config_new_cloned (const NMIP6Config *src)
80 {
81         NMIP6Config *new;
82
83         g_return_val_if_fail (NM_IS_IP6_CONFIG (src), NULL);
84
85         new = nm_ip6_config_new (nm_ip6_config_get_ifindex (src));
86         nm_ip6_config_replace (new, src, NULL);
87         return new;
88 }
89
90 int
91 nm_ip6_config_get_ifindex (const NMIP6Config *config)
92 {
93         return NM_IP6_CONFIG_GET_PRIVATE (config)->ifindex;
94 }
95
96 /******************************************************************/
97
98 static gboolean
99 same_prefix (const struct in6_addr *address1, const struct in6_addr *address2, int plen)
100 {
101         const guint8 *bytes1 = (const guint8 *) address1;
102         const guint8 *bytes2 = (const guint8 *) address2;
103         int nbytes = plen / 8;
104         int nbits = plen % 8;
105         int masked1 = bytes1[nbytes] >> (8 - nbits);
106         int masked2 = bytes2[nbytes] >> (8 - nbits);
107
108         if (nbytes && memcmp (bytes1, bytes2, nbytes))
109                 return FALSE;
110
111         return masked1 == masked2;
112 }
113
114 /******************************************************************/
115
116 /**
117  * nm_ip6_config_capture_resolv_conf():
118  * @nameservers: array of struct in6_addr
119  * @rc_contents: the contents of a resolv.conf or %NULL to read /etc/resolv.conf
120  *
121  * Reads all resolv.conf IPv6 nameservers and adds them to @nameservers.
122  *
123  * Returns: %TRUE if nameservers were added, %FALSE if @nameservers is unchanged
124  */
125 gboolean
126 nm_ip6_config_capture_resolv_conf (GArray *nameservers,
127                                    GPtrArray *dns_options,
128                                    const char *rc_contents)
129 {
130         GPtrArray *read_ns, *read_options;
131         guint i, j;
132         gboolean changed = FALSE;
133
134         g_return_val_if_fail (nameservers != NULL, FALSE);
135
136         read_ns = nm_utils_read_resolv_conf_nameservers (rc_contents);
137         if (!read_ns)
138                 return FALSE;
139
140         for (i = 0; i < read_ns->len; i++) {
141                 const char *s = g_ptr_array_index (read_ns, i);
142                 struct in6_addr ns = IN6ADDR_ANY_INIT;
143
144                 if (!inet_pton (AF_INET6, s, (void *) &ns) || IN6_IS_ADDR_UNSPECIFIED (&ns))
145                         continue;
146
147                 /* Ignore duplicates */
148                 for (j = 0; j < nameservers->len; j++) {
149                         struct in6_addr *t = &g_array_index (nameservers, struct in6_addr, j);
150
151                         if (IN6_ARE_ADDR_EQUAL (t, &ns))
152                                 break;
153                 }
154
155                 if (j == nameservers->len) {
156                         g_array_append_val (nameservers, ns);
157                         changed = TRUE;
158                 }
159         }
160         g_ptr_array_unref (read_ns);
161
162         if (dns_options) {
163                 read_options = nm_utils_read_resolv_conf_dns_options (rc_contents);
164                 if (!read_options)
165                         return changed;
166
167                 for (i = 0; i < read_options->len; i++) {
168                         const char *s = g_ptr_array_index (read_options, i);
169
170                         if (_nm_utils_dns_option_validate (s, NULL, NULL, TRUE, _nm_utils_dns_option_descs) &&
171                                 _nm_utils_dns_option_find_idx (dns_options, s) < 0) {
172                                 g_ptr_array_add (dns_options, g_strdup (s));
173                                 changed = TRUE;
174                         }
175                 }
176                 g_ptr_array_unref (read_options);
177         }
178
179         return changed;
180 }
181
182 static gboolean
183 addresses_are_duplicate (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
184 {
185         return IN6_ARE_ADDR_EQUAL (&a->address, &b->address);
186 }
187
188 static gboolean
189 routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_gateway_and_metric)
190 {
191         return IN6_ARE_ADDR_EQUAL (&a->network, &b->network) && a->plen == b->plen &&
192                (   !consider_gateway_and_metric
193                 || (   IN6_ARE_ADDR_EQUAL (&a->gateway, &b->gateway)
194                     && nm_utils_ip6_route_metric_normalize (a->metric) == nm_utils_ip6_route_metric_normalize (b->metric)));
195 }
196
197 static gint
198 _addresses_sort_cmp_get_prio (const struct in6_addr *addr)
199 {
200         if (IN6_IS_ADDR_V4MAPPED (addr))
201                 return 0;
202         if (IN6_IS_ADDR_V4COMPAT (addr))
203                 return 1;
204         if (IN6_IS_ADDR_UNSPECIFIED (addr))
205                 return 2;
206         if (IN6_IS_ADDR_LOOPBACK (addr))
207                 return 3;
208         if (IN6_IS_ADDR_LINKLOCAL (addr))
209                 return 4;
210         if (IN6_IS_ADDR_SITELOCAL (addr))
211                 return 5;
212         return 6;
213 }
214
215 static gint
216 _addresses_sort_cmp (gconstpointer a, gconstpointer b, gpointer user_data)
217 {
218         gint p1, p2, c;
219         gboolean perm1, perm2, tent1, tent2;
220         gboolean ipv6_privacy1, ipv6_privacy2;
221         const NMPlatformIP6Address *a1 = a, *a2 = b;
222
223         /* tentative addresses are always sorted back... */
224         /* sort tentative addresses after non-tentative. */
225         tent1 = (a1->n_ifa_flags & IFA_F_TENTATIVE);
226         tent2 = (a2->n_ifa_flags & IFA_F_TENTATIVE);
227         if (tent1 != tent2)
228                 return tent1 ? 1 : -1;
229
230         /* Sort by address type. For example link local will
231          * be sorted *after* site local or global. */
232         p1 = _addresses_sort_cmp_get_prio (&a1->address);
233         p2 = _addresses_sort_cmp_get_prio (&a2->address);
234         if (p1 != p2)
235                 return p1 > p2 ? -1 : 1;
236
237         ipv6_privacy1 = !!(a1->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
238         ipv6_privacy2 = !!(a2->n_ifa_flags & (IFA_F_MANAGETEMPADDR | IFA_F_TEMPORARY));
239         if (ipv6_privacy1 || ipv6_privacy2) {
240                 gboolean prefer_temp = ((NMSettingIP6ConfigPrivacy) GPOINTER_TO_INT (user_data)) == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR;
241                 gboolean public1 = TRUE, public2 = TRUE;
242
243                 if (ipv6_privacy1) {
244                         if (a1->n_ifa_flags & IFA_F_TEMPORARY)
245                                 public1 = prefer_temp;
246                         else
247                                 public1 = !prefer_temp;
248                 }
249                 if (ipv6_privacy2) {
250                         if (a2->n_ifa_flags & IFA_F_TEMPORARY)
251                                 public2 = prefer_temp;
252                         else
253                                 public2 = !prefer_temp;
254                 }
255
256                 if (public1 != public2)
257                         return public1 ? -1 : 1;
258         }
259
260         /* Sort the addresses based on their source. */
261         if (a1->source != a2->source)
262                 return a1->source > a2->source ? -1 : 1;
263
264         /* sort permanent addresses before non-permanent. */
265         perm1 = (a1->n_ifa_flags & IFA_F_PERMANENT);
266         perm2 = (a2->n_ifa_flags & IFA_F_PERMANENT);
267         if (perm1 != perm2)
268                 return perm1 ? -1 : 1;
269
270         /* finally sort addresses lexically */
271         c = memcmp (&a1->address, &a2->address, sizeof (a2->address));
272         return c != 0 ? c : memcmp (a1, a2, sizeof (*a1));
273 }
274
275 gboolean
276 nm_ip6_config_addresses_sort (NMIP6Config *self, NMSettingIP6ConfigPrivacy use_temporary)
277 {
278         NMIP6ConfigPrivate *priv;
279         size_t data_len = 0;
280         char *data_pre = NULL;
281         gboolean changed;
282
283         g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE);
284
285         priv = NM_IP6_CONFIG_GET_PRIVATE (self);
286         if (priv->addresses->len > 1) {
287                 data_len = priv->addresses->len * g_array_get_element_size (priv->addresses);
288                 data_pre = g_new (char, data_len);
289                 memcpy (data_pre, priv->addresses->data, data_len);
290
291                 g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary));
292
293                 changed = memcmp (data_pre, priv->addresses->data, data_len) != 0;
294                 g_free (data_pre);
295
296                 if (changed) {
297                         _notify (self, PROP_ADDRESS_DATA);
298                         _notify (self, PROP_ADDRESSES);
299                         return TRUE;
300                 }
301         }
302         return FALSE;
303 }
304
305 NMIP6Config *
306 nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary)
307 {
308         NMIP6Config *config;
309         NMIP6ConfigPrivate *priv;
310         guint i;
311         guint32 lowest_metric = G_MAXUINT32;
312         struct in6_addr old_gateway = IN6ADDR_ANY_INIT;
313         gboolean has_gateway = FALSE;
314         gboolean notify_nameservers = FALSE;
315
316         /* Slaves have no IP configuration */
317         if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
318                 return NULL;
319
320         config = nm_ip6_config_new (ifindex);
321         priv = NM_IP6_CONFIG_GET_PRIVATE (config);
322
323         g_array_unref (priv->addresses);
324         g_array_unref (priv->routes);
325
326         priv->addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
327         priv->routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
328
329         /* Extract gateway from default route */
330         old_gateway = priv->gateway;
331         for (i = 0; i < priv->routes->len; ) {
332                 const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
333
334                 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
335                         if (route->metric < lowest_metric) {
336                                 priv->gateway = route->gateway;
337                                 lowest_metric = route->metric;
338                         }
339                         has_gateway = TRUE;
340                         /* Remove the default route from the list */
341                         g_array_remove_index_fast (priv->routes, i);
342                         continue;
343                 }
344                 i++;
345         }
346
347         /* we detect the route metric based on the default route. All non-default
348          * routes have their route metrics explicitly set. */
349         priv->route_metric = has_gateway ? (gint64) lowest_metric : (gint64) -1;
350
351         /* If there is a host route to the gateway, ignore that route.  It is
352          * automatically added by NetworkManager when needed.
353          */
354         if (has_gateway) {
355                 for (i = 0; i < priv->routes->len; i++) {
356                         const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
357
358                         if (   route->plen == 128
359                             && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway)
360                             && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
361                                 g_array_remove_index (priv->routes, i);
362                                 i--;
363                         }
364                 }
365         }
366
367         /* If the interface has the default route, and has IPv6 addresses, capture
368          * nameservers from /etc/resolv.conf.
369          */
370         if (priv->addresses->len && has_gateway && capture_resolv_conf)
371                 notify_nameservers = nm_ip6_config_capture_resolv_conf (priv->nameservers,
372                                                                         priv->dns_options,
373                                                                         NULL);
374
375         g_array_sort_with_data (priv->addresses, _addresses_sort_cmp, GINT_TO_POINTER (use_temporary));
376
377         /* actually, nobody should be connected to the signal, just to be sure, notify */
378         if (notify_nameservers)
379                 _notify (config, PROP_NAMESERVERS);
380         _notify (config, PROP_ADDRESS_DATA);
381         _notify (config, PROP_ADDRESSES);
382         _notify (config, PROP_ROUTE_DATA);
383         _notify (config, PROP_ROUTES);
384         if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &old_gateway))
385                 _notify (config, PROP_GATEWAY);
386
387         return config;
388 }
389
390 gboolean
391 nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync)
392 {
393         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
394         int i;
395         gboolean success;
396
397         g_return_val_if_fail (ifindex > 0, FALSE);
398         g_return_val_if_fail (config != NULL, FALSE);
399
400         /* Addresses */
401         nm_platform_ip6_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, TRUE);
402
403         /* Routes */
404         {
405                 int count = nm_ip6_config_get_num_routes (config);
406                 GArray *routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), count);
407                 const NMPlatformIP6Route *route;
408
409                 for (i = 0; i < count; i++) {
410                         route = nm_ip6_config_get_route (config, i);
411
412                         /* Don't add the route if it's more specific than one of the subnets
413                          * the device already has an IP address on.
414                          */
415                         if (   IN6_IS_ADDR_UNSPECIFIED (&route->gateway)
416                             && nm_ip6_config_destination_is_direct (config, &route->network, route->plen))
417                                 continue;
418
419                         g_array_append_vals (routes, route, 1);
420                 }
421
422                 success = nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, routes_full_sync);
423                 g_array_unref (routes);
424         }
425
426         return success;
427 }
428
429 void
430 nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
431 {
432         NMIP6ConfigPrivate *priv;
433         guint naddresses, nroutes, nnameservers, nsearches;
434         const char *gateway_str;
435         int i;
436
437         if (!setting)
438                 return;
439
440         g_return_if_fail (NM_IS_SETTING_IP6_CONFIG (setting));
441
442         priv = NM_IP6_CONFIG_GET_PRIVATE (config);
443
444         naddresses = nm_setting_ip_config_get_num_addresses (setting);
445         nroutes = nm_setting_ip_config_get_num_routes (setting);
446         nnameservers = nm_setting_ip_config_get_num_dns (setting);
447         nsearches = nm_setting_ip_config_get_num_dns_searches (setting);
448
449         g_object_freeze_notify (G_OBJECT (config));
450
451         /* Gateway */
452         if (nm_setting_ip_config_get_never_default (setting))
453                 nm_ip6_config_set_never_default (config, TRUE);
454         else if (nm_setting_ip_config_get_ignore_auto_routes (setting))
455                 nm_ip6_config_set_never_default (config, FALSE);
456         gateway_str = nm_setting_ip_config_get_gateway (setting);
457         if (gateway_str) {
458                 struct in6_addr gateway;
459
460                 inet_pton (AF_INET6, gateway_str, &gateway);
461                 nm_ip6_config_set_gateway (config, &gateway);
462         }
463
464         if (priv->route_metric  == -1)
465                 priv->route_metric = nm_setting_ip_config_get_route_metric (setting);
466
467         /* Addresses */
468         for (i = 0; i < naddresses; i++) {
469                 NMIPAddress *s_addr = nm_setting_ip_config_get_address (setting, i);
470                 NMPlatformIP6Address address;
471
472                 memset (&address, 0, sizeof (address));
473                 nm_ip_address_get_address_binary (s_addr, &address.address);
474                 address.plen = nm_ip_address_get_prefix (s_addr);
475                 address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT;
476                 address.preferred = NM_PLATFORM_LIFETIME_PERMANENT;
477                 address.source = NM_IP_CONFIG_SOURCE_USER;
478
479                 nm_ip6_config_add_address (config, &address);
480         }
481
482         /* Routes */
483         if (nm_setting_ip_config_get_ignore_auto_routes (setting))
484                 nm_ip6_config_reset_routes (config);
485         for (i = 0; i < nroutes; i++) {
486                 NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
487                 NMPlatformIP6Route route;
488
489                 memset (&route, 0, sizeof (route));
490                 nm_ip_route_get_dest_binary (s_route, &route.network);
491                 route.plen = nm_ip_route_get_prefix (s_route);
492                 nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
493                 if (nm_ip_route_get_metric (s_route) == -1)
494                         route.metric = default_route_metric;
495                 else
496                         route.metric = nm_ip_route_get_metric (s_route);
497                 route.source = NM_IP_CONFIG_SOURCE_USER;
498
499                 g_assert (route.plen > 0);
500
501                 nm_ip6_config_add_route (config, &route);
502         }
503
504         /* DNS */
505         if (nm_setting_ip_config_get_ignore_auto_dns (setting)) {
506                 nm_ip6_config_reset_nameservers (config);
507                 nm_ip6_config_reset_domains (config);
508                 nm_ip6_config_reset_searches (config);
509         }
510         for (i = 0; i < nnameservers; i++) {
511                  struct in6_addr ip;
512
513                 if (inet_pton (AF_INET6, nm_setting_ip_config_get_dns (setting, i), &ip) == 1)
514                         nm_ip6_config_add_nameserver (config, &ip);
515         }
516         for (i = 0; i < nsearches; i++)
517                 nm_ip6_config_add_search (config, nm_setting_ip_config_get_dns_search (setting, i));
518
519         i = 0;
520         while ((i = nm_setting_ip_config_next_valid_dns_option (setting, i)) >= 0) {
521                 nm_ip6_config_add_dns_option (config, nm_setting_ip_config_get_dns_option (setting, i));
522                 i++;
523         }
524
525         g_object_thaw_notify (G_OBJECT (config));
526 }
527
528 NMSetting *
529 nm_ip6_config_create_setting (const NMIP6Config *config)
530 {
531         NMSettingIPConfig *s_ip6;
532         const struct in6_addr *gateway;
533         guint naddresses, nroutes, nnameservers, nsearches, noptions;
534         const char *method = NULL;
535         int i;
536         gint64 route_metric;
537
538         s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
539
540         if (!config) {
541                 g_object_set (s_ip6,
542                               NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
543                               NULL);
544                 return NM_SETTING (s_ip6);
545         }
546
547         gateway = nm_ip6_config_get_gateway (config);
548         naddresses = nm_ip6_config_get_num_addresses (config);
549         nroutes = nm_ip6_config_get_num_routes (config);
550         nnameservers = nm_ip6_config_get_num_nameservers (config);
551         nsearches = nm_ip6_config_get_num_searches (config);
552         noptions = nm_ip6_config_get_num_dns_options (config);
553         route_metric = nm_ip6_config_get_route_metric (config);
554
555         /* Addresses */
556         for (i = 0; i < naddresses; i++) {
557                 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
558                 NMIPAddress *s_addr;
559
560                 /* Ignore link-local address. */
561                 if (IN6_IS_ADDR_LINKLOCAL (&address->address)) {
562                         if (!method)
563                                 method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
564                         continue;
565                 }
566
567                 /* Detect dynamic address */
568                 if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
569                         method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
570                         continue;
571                 }
572
573                 /* Static address found. */
574                 if (!method || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
575                         method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
576
577                 s_addr = nm_ip_address_new_binary (AF_INET6, &address->address, address->plen, NULL);
578                 nm_setting_ip_config_add_address (s_ip6, s_addr);
579                 nm_ip_address_unref (s_addr);
580         }
581
582         /* Gateway */
583         if (   gateway
584             && nm_setting_ip_config_get_num_addresses (s_ip6) > 0) {
585                 g_object_set (s_ip6,
586                               NM_SETTING_IP_CONFIG_GATEWAY, nm_utils_inet6_ntop (gateway, NULL),
587                               NULL);
588         }
589
590         /* Use 'ignore' if the method wasn't previously set */
591         if (!method)
592                 method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
593
594         g_object_set (s_ip6,
595                       NM_SETTING_IP_CONFIG_METHOD, method,
596                       NM_SETTING_IP_CONFIG_ROUTE_METRIC, (gint64) route_metric,
597                       NULL);
598
599         /* Routes */
600         for (i = 0; i < nroutes; i++) {
601                 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
602                 NMIPRoute *s_route;
603
604                 /* Ignore link-local route. */
605                 if (IN6_IS_ADDR_LINKLOCAL (&route->network))
606                         continue;
607
608                 /* Ignore default route. */
609                 if (!route->plen)
610                         continue;
611
612                 /* Ignore routes provided by external sources */
613                 if (route->source != NM_IP_CONFIG_SOURCE_USER)
614                         continue;
615
616                 s_route = nm_ip_route_new_binary (AF_INET6,
617                                                   &route->network, route->plen,
618                                                   &route->gateway, route->metric,
619                                                   NULL);
620                 nm_setting_ip_config_add_route (s_ip6, s_route);
621                 nm_ip_route_unref (s_route);
622         }
623
624         /* DNS */
625         for (i = 0; i < nnameservers; i++) {
626                 const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (config, i);
627
628                 nm_setting_ip_config_add_dns (s_ip6, nm_utils_inet6_ntop (nameserver, NULL));
629         }
630         for (i = 0; i < nsearches; i++) {
631                 const char *search = nm_ip6_config_get_search (config, i);
632
633                 nm_setting_ip_config_add_dns_search (s_ip6, search);
634         }
635         for (i = 0; i < noptions; i++) {
636                 const char *option = nm_ip6_config_get_dns_option (config, i);
637
638                 nm_setting_ip_config_add_dns_option (s_ip6, option);
639         }
640
641
642         return NM_SETTING (s_ip6);
643 }
644
645 /******************************************************************/
646
647 void
648 nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src, NMIPConfigMergeFlags merge_flags)
649 {
650         NMIP6ConfigPrivate *dst_priv, *src_priv;
651         guint32 i;
652
653         g_return_if_fail (src != NULL);
654         g_return_if_fail (dst != NULL);
655
656         dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
657         src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
658
659         g_object_freeze_notify (G_OBJECT (dst));
660
661         /* addresses */
662         for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++)
663                 nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
664
665         /* nameservers */
666         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
667                 for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++)
668                         nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
669         }
670
671         /* default gateway */
672         if (nm_ip6_config_get_gateway (src))
673                 nm_ip6_config_set_gateway (dst, nm_ip6_config_get_gateway (src));
674
675         /* routes */
676         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_ROUTES)) {
677                 for (i = 0; i < nm_ip6_config_get_num_routes (src); i++)
678                         nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
679         }
680
681         if (dst_priv->route_metric == -1)
682                 dst_priv->route_metric = src_priv->route_metric;
683         else if (src_priv->route_metric != -1)
684                 dst_priv->route_metric = MIN (dst_priv->route_metric, src_priv->route_metric);
685
686         /* domains */
687         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
688                 for (i = 0; i < nm_ip6_config_get_num_domains (src); i++)
689                         nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
690         }
691
692         /* dns searches */
693         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
694                 for (i = 0; i < nm_ip6_config_get_num_searches (src); i++)
695                         nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
696         }
697
698         /* dns options */
699         if (!NM_FLAGS_HAS (merge_flags, NM_IP_CONFIG_MERGE_NO_DNS)) {
700                 for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++)
701                         nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
702         }
703
704         if (nm_ip6_config_get_mss (src))
705                 nm_ip6_config_set_mss (dst, nm_ip6_config_get_mss (src));
706
707         g_object_thaw_notify (G_OBJECT (dst));
708 }
709
710 gboolean
711 nm_ip6_config_destination_is_direct (const NMIP6Config *config, const struct in6_addr *network, int plen)
712 {
713         int num = nm_ip6_config_get_num_addresses (config);
714         int i;
715
716         for (i = 0; i < num; i++) {
717                 const NMPlatformIP6Address *item = nm_ip6_config_get_address (config, i);
718
719                 if (item->plen <= plen && same_prefix (&item->address, network, item->plen) &&
720                     !(item->n_ifa_flags & IFA_F_NOPREFIXROUTE))
721                         return TRUE;
722         }
723
724         return FALSE;
725 }
726
727 /*******************************************************************************/
728
729 static int
730 _addresses_get_index (const NMIP6Config *self, const NMPlatformIP6Address *addr)
731 {
732         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
733         guint i;
734
735         for (i = 0; i < priv->addresses->len; i++) {
736                 const NMPlatformIP6Address *a = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
737
738                 if (addresses_are_duplicate (a, addr))
739                         return (int) i;
740         }
741         return -1;
742 }
743
744 static int
745 _nameservers_get_index (const NMIP6Config *self, const struct in6_addr *ns)
746 {
747         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
748         guint i;
749
750         for (i = 0; i < priv->nameservers->len; i++) {
751                 const struct in6_addr *n = &g_array_index (priv->nameservers, struct in6_addr, i);
752
753                 if (IN6_ARE_ADDR_EQUAL (ns, n))
754                         return (int) i;
755         }
756         return -1;
757 }
758
759 static int
760 _routes_get_index (const NMIP6Config *self, const NMPlatformIP6Route *route)
761 {
762         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
763         guint i;
764
765         for (i = 0; i < priv->routes->len; i++) {
766                 const NMPlatformIP6Route *r = &g_array_index (priv->routes, NMPlatformIP6Route, i);
767
768                 if (routes_are_duplicate (route, r, FALSE))
769                         return (int) i;
770         }
771         return -1;
772 }
773
774 static int
775 _domains_get_index (const NMIP6Config *self, const char *domain)
776 {
777         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
778         guint i;
779
780         for (i = 0; i < priv->domains->len; i++) {
781                 const char *d = g_ptr_array_index (priv->domains, i);
782
783                 if (g_strcmp0 (domain, d) == 0)
784                         return (int) i;
785         }
786         return -1;
787 }
788
789 static int
790 _searches_get_index (const NMIP6Config *self, const char *search)
791 {
792         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
793         guint i;
794
795         for (i = 0; i < priv->searches->len; i++) {
796                 const char *s = g_ptr_array_index (priv->searches, i);
797
798                 if (g_strcmp0 (search, s) == 0)
799                         return (int) i;
800         }
801         return -1;
802 }
803
804 static int
805 _dns_options_get_index (const NMIP6Config *self, const char *option)
806 {
807         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
808         guint i;
809
810         for (i = 0; i < priv->dns_options->len; i++) {
811                 const char *s = g_ptr_array_index (priv->dns_options, i);
812
813                 if (g_strcmp0 (option, s) == 0)
814                         return (int) i;
815         }
816         return -1;
817 }
818
819 /*******************************************************************************/
820
821 /**
822  * nm_ip6_config_subtract:
823  * @dst: config from which to remove everything in @src
824  * @src: config to remove from @dst
825  *
826  * Removes everything in @src from @dst.
827  */
828 void
829 nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src)
830 {
831         guint i;
832         gint idx;
833         const struct in6_addr *dst_tmp, *src_tmp;
834
835         g_return_if_fail (src != NULL);
836         g_return_if_fail (dst != NULL);
837
838         g_object_freeze_notify (G_OBJECT (dst));
839
840         /* addresses */
841         for (i = 0; i < nm_ip6_config_get_num_addresses (src); i++) {
842                 idx = _addresses_get_index (dst, nm_ip6_config_get_address (src, i));
843                 if (idx >= 0)
844                         nm_ip6_config_del_address (dst, idx);
845         }
846
847         /* nameservers */
848         for (i = 0; i < nm_ip6_config_get_num_nameservers (src); i++) {
849                 idx = _nameservers_get_index (dst, nm_ip6_config_get_nameserver (src, i));
850                 if (idx >= 0)
851                         nm_ip6_config_del_nameserver (dst, idx);
852         }
853
854         /* default gateway */
855         src_tmp = nm_ip6_config_get_gateway (src);
856         dst_tmp = nm_ip6_config_get_gateway (dst);
857         if (src_tmp && dst_tmp && IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp))
858                 nm_ip6_config_set_gateway (dst, NULL);
859
860         if (!nm_ip6_config_get_num_addresses (dst))
861                 nm_ip6_config_set_gateway (dst, NULL);
862
863         /* ignore route_metric */
864
865         /* routes */
866         for (i = 0; i < nm_ip6_config_get_num_routes (src); i++) {
867                 idx = _routes_get_index (dst, nm_ip6_config_get_route (src, i));
868                 if (idx >= 0)
869                         nm_ip6_config_del_route (dst, idx);
870         }
871
872         /* domains */
873         for (i = 0; i < nm_ip6_config_get_num_domains (src); i++) {
874                 idx = _domains_get_index (dst, nm_ip6_config_get_domain (src, i));
875                 if (idx >= 0)
876                         nm_ip6_config_del_domain (dst, idx);
877         }
878
879         /* dns searches */
880         for (i = 0; i < nm_ip6_config_get_num_searches (src); i++) {
881                 idx = _searches_get_index (dst, nm_ip6_config_get_search (src, i));
882                 if (idx >= 0)
883                         nm_ip6_config_del_search (dst, idx);
884         }
885
886         /* dns options */
887         for (i = 0; i < nm_ip6_config_get_num_dns_options (src); i++) {
888                 idx = _dns_options_get_index (dst, nm_ip6_config_get_dns_option (src, i));
889                 if (idx >= 0)
890                         nm_ip6_config_del_dns_option (dst, idx);
891         }
892
893         if (nm_ip6_config_get_mss (src) == nm_ip6_config_get_mss (dst))
894                 nm_ip6_config_set_mss (dst, 0);
895
896         g_object_thaw_notify (G_OBJECT (dst));
897 }
898
899 void
900 nm_ip6_config_intersect (NMIP6Config *dst, const NMIP6Config *src)
901 {
902         guint i;
903         gint idx;
904         const struct in6_addr *dst_tmp, *src_tmp;
905
906         g_return_if_fail (src != NULL);
907         g_return_if_fail (dst != NULL);
908
909         g_object_freeze_notify (G_OBJECT (dst));
910
911         /* addresses */
912         for (i = 0; i < nm_ip6_config_get_num_addresses (dst); ) {
913                 idx = _addresses_get_index (src, nm_ip6_config_get_address (dst, i));
914                 if (idx < 0)
915                         nm_ip6_config_del_address (dst, i);
916                 else
917                         i++;
918         }
919
920         /* ignore route_metric */
921         /* ignore nameservers */
922
923         /* default gateway */
924         dst_tmp = nm_ip6_config_get_gateway (dst);
925         if (dst_tmp) {
926                 src_tmp = nm_ip6_config_get_gateway (src);
927                 if (   !nm_ip6_config_get_num_addresses (dst)
928                     || !src_tmp
929                     || !IN6_ARE_ADDR_EQUAL (src_tmp, dst_tmp))
930                         nm_ip6_config_set_gateway (dst, NULL);
931         }
932
933         /* routes */
934         for (i = 0; i < nm_ip6_config_get_num_routes (dst); ) {
935                 idx = _routes_get_index (src, nm_ip6_config_get_route (dst, i));
936                 if (idx < 0)
937                         nm_ip6_config_del_route (dst, i);
938                 else
939                         i++;
940         }
941
942         /* ignore domains */
943         /* ignore dns searches */
944         /* ignome dns options */
945
946         g_object_thaw_notify (G_OBJECT (dst));
947 }
948
949 /**
950  * nm_ip6_config_replace:
951  * @dst: config which will be replaced with everything in @src
952  * @src: config to copy over to @dst
953  * @relevant_changes: return whether there are changes to the
954  * destination object that are relevant. This is equal to
955  * nm_ip6_config_equal() showing any difference.
956  *
957  * Replaces everything in @dst with @src so that the two configurations
958  * contain the same content -- with the exception of the dbus path.
959  *
960  * Returns: whether the @dst instance changed in any way (including minor changes,
961  * that are not signaled by the output parameter @relevant_changes).
962  */
963 gboolean
964 nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relevant_changes)
965 {
966 #if NM_MORE_ASSERTS
967         gboolean config_equal;
968 #endif
969         gboolean has_minor_changes = FALSE, has_relevant_changes = FALSE, are_equal;
970         guint i, num;
971         NMIP6ConfigPrivate *dst_priv, *src_priv;
972         const NMPlatformIP6Address *dst_addr, *src_addr;
973         const NMPlatformIP6Route *dst_route, *src_route;
974
975         g_return_val_if_fail (NM_IS_IP6_CONFIG (src), FALSE);
976         g_return_val_if_fail (NM_IS_IP6_CONFIG (dst), FALSE);
977         g_return_val_if_fail (src != dst, FALSE);
978
979 #if NM_MORE_ASSERTS
980         config_equal = nm_ip6_config_equal (dst, src);
981 #endif
982
983         dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst);
984         src_priv = NM_IP6_CONFIG_GET_PRIVATE (src);
985
986         g_object_freeze_notify (G_OBJECT (dst));
987
988         /* ifindex */
989         if (src_priv->ifindex != dst_priv->ifindex) {
990                 dst_priv->ifindex = src_priv->ifindex;
991                 has_minor_changes = TRUE;
992         }
993
994         /* never_default */
995         if (src_priv->never_default != dst_priv->never_default) {
996                 dst_priv->never_default = src_priv->never_default;
997                 has_minor_changes = TRUE;
998         }
999
1000         /* default gateway */
1001         if (!IN6_ARE_ADDR_EQUAL (&src_priv->gateway, &dst_priv->gateway)) {
1002                 nm_ip6_config_set_gateway (dst, &src_priv->gateway);
1003                 has_relevant_changes = TRUE;
1004         }
1005
1006         if (src_priv->route_metric != dst_priv->route_metric) {
1007                 dst_priv->route_metric = src_priv->route_metric;
1008                 has_minor_changes = TRUE;
1009         }
1010
1011         /* addresses */
1012         num = nm_ip6_config_get_num_addresses (src);
1013         are_equal = num == nm_ip6_config_get_num_addresses (dst);
1014         if (are_equal) {
1015                 for (i = 0; i < num; i++ ) {
1016                         if (nm_platform_ip6_address_cmp (src_addr = nm_ip6_config_get_address (src, i),
1017                                                          dst_addr = nm_ip6_config_get_address (dst, i))) {
1018                                 are_equal = FALSE;
1019                                 if (   !addresses_are_duplicate (src_addr, dst_addr)
1020                                     || src_addr->plen != dst_addr->plen
1021                                     || !IN6_ARE_ADDR_EQUAL (nm_platform_ip6_address_get_peer (src_addr),
1022                                                             nm_platform_ip6_address_get_peer (dst_addr)))  {
1023                                         has_relevant_changes = TRUE;
1024                                         break;
1025                                 }
1026                         }
1027                 }
1028         } else
1029                 has_relevant_changes = TRUE;
1030         if (!are_equal) {
1031                 nm_ip6_config_reset_addresses (dst);
1032                 for (i = 0; i < num; i++)
1033                         nm_ip6_config_add_address (dst, nm_ip6_config_get_address (src, i));
1034                 has_minor_changes = TRUE;
1035         }
1036
1037         /* routes */
1038         num = nm_ip6_config_get_num_routes (src);
1039         are_equal = num == nm_ip6_config_get_num_routes (dst);
1040         if (are_equal) {
1041                 for (i = 0; i < num; i++ ) {
1042                         if (nm_platform_ip6_route_cmp (src_route = nm_ip6_config_get_route (src, i),
1043                                                        dst_route = nm_ip6_config_get_route (dst, i))) {
1044                                 are_equal = FALSE;
1045                                 if (!routes_are_duplicate (src_route, dst_route, TRUE)) {
1046                                         has_relevant_changes = TRUE;
1047                                         break;
1048                                 }
1049                         }
1050                 }
1051         } else
1052                 has_relevant_changes = TRUE;
1053         if (!are_equal) {
1054                 nm_ip6_config_reset_routes (dst);
1055                 for (i = 0; i < num; i++)
1056                         nm_ip6_config_add_route (dst, nm_ip6_config_get_route (src, i));
1057                 has_minor_changes = TRUE;
1058         }
1059
1060         /* nameservers */
1061         num = nm_ip6_config_get_num_nameservers (src);
1062         are_equal = num == nm_ip6_config_get_num_nameservers (dst);
1063         if (are_equal) {
1064                 for (i = 0; i < num; i++ ) {
1065                         if (!IN6_ARE_ADDR_EQUAL (nm_ip6_config_get_nameserver (src, i),
1066                                                  nm_ip6_config_get_nameserver (dst, i))) {
1067                                 are_equal = FALSE;
1068                                 break;
1069                         }
1070                 }
1071         }
1072         if (!are_equal) {
1073                 nm_ip6_config_reset_nameservers (dst);
1074                 for (i = 0; i < num; i++)
1075                         nm_ip6_config_add_nameserver (dst, nm_ip6_config_get_nameserver (src, i));
1076                 has_relevant_changes = TRUE;
1077         }
1078
1079         /* domains */
1080         num = nm_ip6_config_get_num_domains (src);
1081         are_equal = num == nm_ip6_config_get_num_domains (dst);
1082         if (are_equal) {
1083                 for (i = 0; i < num; i++ ) {
1084                         if (g_strcmp0 (nm_ip6_config_get_domain (src, i),
1085                                         nm_ip6_config_get_domain (dst, i))) {
1086                                 are_equal = FALSE;
1087                                 break;
1088                         }
1089                 }
1090         }
1091         if (!are_equal) {
1092                 nm_ip6_config_reset_domains (dst);
1093                 for (i = 0; i < num; i++)
1094                         nm_ip6_config_add_domain (dst, nm_ip6_config_get_domain (src, i));
1095                 has_relevant_changes = TRUE;
1096         }
1097
1098         /* dns searches */
1099         num = nm_ip6_config_get_num_searches (src);
1100         are_equal = num == nm_ip6_config_get_num_searches (dst);
1101         if (are_equal) {
1102                 for (i = 0; i < num; i++ ) {
1103                         if (g_strcmp0 (nm_ip6_config_get_search (src, i),
1104                                         nm_ip6_config_get_search (dst, i))) {
1105                                 are_equal = FALSE;
1106                                 break;
1107                         }
1108                 }
1109         }
1110         if (!are_equal) {
1111                 nm_ip6_config_reset_searches (dst);
1112                 for (i = 0; i < num; i++)
1113                         nm_ip6_config_add_search (dst, nm_ip6_config_get_search (src, i));
1114                 has_relevant_changes = TRUE;
1115         }
1116
1117         /* dns options */
1118         num = nm_ip6_config_get_num_dns_options (src);
1119         are_equal = num == nm_ip6_config_get_num_dns_options (dst);
1120         if (are_equal) {
1121                 for (i = 0; i < num; i++ ) {
1122                         if (g_strcmp0 (nm_ip6_config_get_dns_option (src, i),
1123                                        nm_ip6_config_get_dns_option (dst, i))) {
1124                                 are_equal = FALSE;
1125                                 break;
1126                         }
1127                 }
1128         }
1129         if (!are_equal) {
1130                 nm_ip6_config_reset_dns_options (dst);
1131                 for (i = 0; i < num; i++)
1132                         nm_ip6_config_add_dns_option (dst, nm_ip6_config_get_dns_option (src, i));
1133                 has_relevant_changes = TRUE;
1134         }
1135
1136         /* mss */
1137         if (src_priv->mss != dst_priv->mss) {
1138                 nm_ip6_config_set_mss (dst, src_priv->mss);
1139                 has_minor_changes = TRUE;
1140         }
1141
1142 #if NM_MORE_ASSERTS
1143         /* config_equal does not compare *all* the fields, therefore, we might have has_minor_changes
1144          * regardless of config_equal. But config_equal must correspond to has_relevant_changes. */
1145         nm_assert (config_equal == !has_relevant_changes);
1146 #endif
1147
1148         g_object_thaw_notify (G_OBJECT (dst));
1149
1150         if (relevant_changes)
1151                 *relevant_changes = has_relevant_changes;
1152
1153         return has_relevant_changes || has_minor_changes;
1154 }
1155
1156 void
1157 nm_ip6_config_dump (const NMIP6Config *config, const char *detail)
1158 {
1159         const struct in6_addr *tmp;
1160         guint32 i;
1161         const char *str;
1162
1163         g_return_if_fail (config != NULL);
1164
1165         g_message ("--------- NMIP6Config %p (%s)", config, detail);
1166
1167         str = nm_exported_object_get_path (NM_EXPORTED_OBJECT (config));
1168         if (str)
1169                 g_message ("   path: %s", str);
1170
1171         /* addresses */
1172         for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++)
1173                 g_message ("      a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0));
1174
1175         /* default gateway */
1176         tmp = nm_ip6_config_get_gateway (config);
1177         if (tmp)
1178                 g_message ("     gw: %s", nm_utils_inet6_ntop (tmp, NULL));
1179
1180         /* nameservers */
1181         for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++) {
1182                 tmp = nm_ip6_config_get_nameserver (config, i);
1183                 g_message ("     ns: %s", nm_utils_inet6_ntop (tmp, NULL));
1184         }
1185
1186         /* routes */
1187         for (i = 0; i < nm_ip6_config_get_num_routes (config); i++)
1188                 g_message ("     rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0));
1189
1190         /* domains */
1191         for (i = 0; i < nm_ip6_config_get_num_domains (config); i++)
1192                 g_message (" domain: %s", nm_ip6_config_get_domain (config, i));
1193
1194         /* dns searches */
1195         for (i = 0; i < nm_ip6_config_get_num_searches (config); i++)
1196                 g_message (" search: %s", nm_ip6_config_get_search (config, i));
1197
1198         /* dns options */
1199         for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++)
1200                 g_message (" dnsopt: %s", nm_ip6_config_get_dns_option (config, i));
1201
1202         g_message ("    mss: %"G_GUINT32_FORMAT, nm_ip6_config_get_mss (config));
1203         g_message (" n-dflt: %d", nm_ip6_config_get_never_default (config));
1204 }
1205
1206 /******************************************************************/
1207
1208 void
1209 nm_ip6_config_set_never_default (NMIP6Config *config, gboolean never_default)
1210 {
1211         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1212
1213         priv->never_default = !!never_default;
1214 }
1215
1216 gboolean
1217 nm_ip6_config_get_never_default (const NMIP6Config *config)
1218 {
1219         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1220
1221         return priv->never_default;
1222 }
1223
1224 void
1225 nm_ip6_config_set_gateway (NMIP6Config *config, const struct in6_addr *gateway)
1226 {
1227         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1228
1229         if (gateway) {
1230                 if (IN6_ARE_ADDR_EQUAL (&priv->gateway, gateway))
1231                         return;
1232                 priv->gateway = *gateway;
1233         } else {
1234                 if (IN6_IS_ADDR_UNSPECIFIED (&priv->gateway))
1235                         return;
1236                 memset (&priv->gateway, 0, sizeof (priv->gateway));
1237         }
1238         _notify (config, PROP_GATEWAY);
1239 }
1240
1241 const struct in6_addr *
1242 nm_ip6_config_get_gateway (const NMIP6Config *config)
1243 {
1244         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1245
1246         return IN6_IS_ADDR_UNSPECIFIED (&priv->gateway) ? NULL : &priv->gateway;
1247 }
1248
1249 gint64
1250 nm_ip6_config_get_route_metric (const NMIP6Config *config)
1251 {
1252         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1253
1254         return priv->route_metric;
1255 }
1256
1257 /******************************************************************/
1258
1259 void
1260 nm_ip6_config_reset_addresses (NMIP6Config *config)
1261 {
1262         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1263
1264         if (priv->addresses->len != 0) {
1265                 g_array_set_size (priv->addresses, 0);
1266                 _notify (config, PROP_ADDRESS_DATA);
1267                 _notify (config, PROP_ADDRESSES);
1268         }
1269 }
1270
1271 /**
1272  * nm_ip6_config_add_address:
1273  * @config: the #NMIP6Config
1274  * @new: the new address to add to @config
1275  *
1276  * Adds the new address to @config.  If an address with the same basic properties
1277  * (address, prefix) already exists in @config, it is overwritten with the
1278  * lifetime and preferred of @new.  The source is also overwritten by the source
1279  * from @new if that source is higher priority.
1280  */
1281 void
1282 nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address *new)
1283 {
1284         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1285         NMPlatformIP6Address item_old;
1286         int i;
1287
1288         g_return_if_fail (new != NULL);
1289
1290         for (i = 0; i < priv->addresses->len; i++ ) {
1291                 NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1292
1293                 if (addresses_are_duplicate (item, new)) {
1294                         if (nm_platform_ip6_address_cmp (item, new) == 0)
1295                                 return;
1296
1297                         /* remember the old values. */
1298                         item_old = *item;
1299                         /* Copy over old item to get new lifetime, timestamp, preferred */
1300                         *item = *new;
1301
1302                         /* But restore highest priority source */
1303                         item->source = MAX (item_old.source, new->source);
1304
1305                         /* for addresses that we read from the kernel, we keep the timestamps as defined
1306                          * by the previous source (item_old). The reason is, that the other source configured the lifetimes
1307                          * with "what should be" and the kernel values are "what turned out after configuring it".
1308                          *
1309                          * For other sources, the longer lifetime wins. */
1310                         if (   (new->source == NM_IP_CONFIG_SOURCE_KERNEL && new->source != item_old.source)
1311                             || nm_platform_ip_address_cmp_expiry ((const NMPlatformIPAddress *) &item_old, (const NMPlatformIPAddress *) new) > 0) {
1312                                 item->timestamp = item_old.timestamp;
1313                                 item->lifetime = item_old.lifetime;
1314                                 item->preferred = item_old.preferred;
1315                         }
1316                         if (nm_platform_ip6_address_cmp (&item_old, item) == 0)
1317                                 return;
1318                         goto NOTIFY;
1319                 }
1320         }
1321
1322         g_array_append_val (priv->addresses, *new);
1323 NOTIFY:
1324         _notify (config, PROP_ADDRESS_DATA);
1325         _notify (config, PROP_ADDRESSES);
1326 }
1327
1328 void
1329 nm_ip6_config_del_address (NMIP6Config *config, guint i)
1330 {
1331         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1332
1333         g_return_if_fail (i < priv->addresses->len);
1334
1335         g_array_remove_index (priv->addresses, i);
1336         _notify (config, PROP_ADDRESS_DATA);
1337         _notify (config, PROP_ADDRESSES);
1338 }
1339
1340 guint
1341 nm_ip6_config_get_num_addresses (const NMIP6Config *config)
1342 {
1343         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1344
1345         return priv->addresses->len;
1346 }
1347
1348 const NMPlatformIP6Address *
1349 nm_ip6_config_get_address (const NMIP6Config *config, guint i)
1350 {
1351         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1352
1353         return &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1354 }
1355
1356 gboolean
1357 nm_ip6_config_address_exists (const NMIP6Config *config,
1358                               const NMPlatformIP6Address *needle)
1359 {
1360         return _addresses_get_index (config, needle) >= 0;
1361 }
1362
1363 const NMPlatformIP6Address *
1364 nm_ip6_config_get_address_first_nontentative (const NMIP6Config *config, gboolean linklocal)
1365 {
1366         NMIP6ConfigPrivate *priv;
1367         guint i;
1368
1369         g_return_val_if_fail (NM_IS_IP6_CONFIG (config), NULL);
1370
1371         priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1372
1373         linklocal = !!linklocal;
1374
1375         for (i = 0; i < priv->addresses->len; i++) {
1376                 const NMPlatformIP6Address *addr = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1377
1378                 if (   ((!!IN6_IS_ADDR_LINKLOCAL (&addr->address)) == linklocal)
1379                     && !(addr->n_ifa_flags & IFA_F_TENTATIVE))
1380                         return addr;
1381         }
1382
1383         return NULL;
1384 }
1385
1386 /******************************************************************/
1387
1388 void
1389 nm_ip6_config_reset_routes (NMIP6Config *config)
1390 {
1391         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1392
1393         if (priv->routes->len != 0) {
1394                 g_array_set_size (priv->routes, 0);
1395                 _notify (config, PROP_ROUTE_DATA);
1396                 _notify (config, PROP_ROUTES);
1397         }
1398 }
1399
1400 /**
1401  * nm_ip6_config_add_route:
1402  * @config: the #NMIP6Config
1403  * @new: the new route to add to @config
1404  *
1405  * Adds the new route to @config.  If a route with the same basic properties
1406  * (network, prefix) already exists in @config, it is overwritten including the
1407  * gateway and metric of @new.  The source is also overwritten by the source
1408  * from @new if that source is higher priority.
1409  */
1410 void
1411 nm_ip6_config_add_route (NMIP6Config *config, const NMPlatformIP6Route *new)
1412 {
1413         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1414         NMIPConfigSource old_source;
1415         int i;
1416
1417         g_return_if_fail (new != NULL);
1418         g_return_if_fail (new->plen > 0);
1419         g_assert (priv->ifindex);
1420
1421         for (i = 0; i < priv->routes->len; i++ ) {
1422                 NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
1423
1424                 if (routes_are_duplicate (item, new, FALSE)) {
1425                         if (nm_platform_ip6_route_cmp (item, new) == 0)
1426                                 return;
1427                         old_source = item->source;
1428                         *item = *new;
1429                         /* Restore highest priority source */
1430                         item->source = MAX (old_source, new->source);
1431                         item->ifindex = priv->ifindex;
1432                         goto NOTIFY;
1433                 }
1434         }
1435
1436         g_array_append_val (priv->routes, *new);
1437         g_array_index (priv->routes, NMPlatformIP6Route, priv->routes->len - 1).ifindex = priv->ifindex;
1438 NOTIFY:
1439         _notify (config, PROP_ROUTE_DATA);
1440         _notify (config, PROP_ROUTES);
1441 }
1442
1443 void
1444 nm_ip6_config_del_route (NMIP6Config *config, guint i)
1445 {
1446         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1447
1448         g_return_if_fail (i < priv->routes->len);
1449
1450         g_array_remove_index (priv->routes, i);
1451         _notify (config, PROP_ROUTE_DATA);
1452         _notify (config, PROP_ROUTES);
1453 }
1454
1455 guint
1456 nm_ip6_config_get_num_routes (const NMIP6Config *config)
1457 {
1458         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1459
1460         return priv->routes->len;
1461 }
1462
1463 const NMPlatformIP6Route *
1464 nm_ip6_config_get_route (const NMIP6Config *config, guint i)
1465 {
1466         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1467
1468         return &g_array_index (priv->routes, NMPlatformIP6Route, i);
1469 }
1470
1471 const NMPlatformIP6Route *
1472 nm_ip6_config_get_direct_route_for_host (const NMIP6Config *config, const struct in6_addr *host)
1473 {
1474         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1475         guint i;
1476         struct in6_addr network2, host2;
1477         NMPlatformIP6Route *best_route = NULL;
1478
1479         g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL);
1480
1481         for (i = 0; i < priv->routes->len; i++) {
1482                 NMPlatformIP6Route *item = &g_array_index (priv->routes, NMPlatformIP6Route, i);
1483
1484                 if (!IN6_IS_ADDR_UNSPECIFIED (&item->gateway))
1485                         continue;
1486
1487                 if (best_route && best_route->plen > item->plen)
1488                         continue;
1489
1490                 nm_utils_ip6_address_clear_host_address (&host2, host, item->plen);
1491                 nm_utils_ip6_address_clear_host_address (&network2, &item->network, item->plen);
1492
1493                 if (!IN6_ARE_ADDR_EQUAL (&network2, &host2))
1494                         continue;
1495
1496                 if (best_route &&
1497                     nm_utils_ip6_route_metric_normalize (best_route->metric) <= nm_utils_ip6_route_metric_normalize (item->metric))
1498                         continue;
1499
1500                 best_route = item;
1501         }
1502
1503         return best_route;
1504 }
1505
1506 const NMPlatformIP6Address *
1507 nm_ip6_config_get_subnet_for_host (const NMIP6Config *config, const struct in6_addr *host)
1508 {
1509         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1510         guint i;
1511         NMPlatformIP6Address *subnet = NULL;
1512         struct in6_addr subnet2, host2;
1513
1514         g_return_val_if_fail (host && !IN6_IS_ADDR_UNSPECIFIED (host), NULL);
1515
1516         for (i = 0; i < priv->addresses->len; i++) {
1517                 NMPlatformIP6Address *item = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
1518
1519                 if (subnet && subnet->plen >= item->plen)
1520                         continue;
1521
1522                 nm_utils_ip6_address_clear_host_address (&host2, host, item->plen);
1523                 nm_utils_ip6_address_clear_host_address (&subnet2, &item->address, item->plen);
1524
1525                 if (IN6_ARE_ADDR_EQUAL (&subnet2, &host2))
1526                         subnet = item;
1527         }
1528
1529         return subnet;
1530 }
1531
1532
1533 /******************************************************************/
1534
1535 void
1536 nm_ip6_config_reset_nameservers (NMIP6Config *config)
1537 {
1538         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1539
1540         if (priv->nameservers->len != 0) {
1541                 g_array_set_size (priv->nameservers, 0);
1542                 _notify (config, PROP_NAMESERVERS);
1543         }
1544 }
1545
1546 void
1547 nm_ip6_config_add_nameserver (NMIP6Config *config, const struct in6_addr *new)
1548 {
1549         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1550         int i;
1551
1552         g_return_if_fail (new != NULL);
1553
1554         for (i = 0; i < priv->nameservers->len; i++)
1555                 if (IN6_ARE_ADDR_EQUAL (new, &g_array_index (priv->nameservers, struct in6_addr, i)))
1556                         return;
1557
1558         g_array_append_val (priv->nameservers, *new);
1559         _notify (config, PROP_NAMESERVERS);
1560 }
1561
1562 void
1563 nm_ip6_config_del_nameserver (NMIP6Config *config, guint i)
1564 {
1565         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1566
1567         g_return_if_fail (i < priv->nameservers->len);
1568
1569         g_array_remove_index (priv->nameservers, i);
1570         _notify (config, PROP_NAMESERVERS);
1571 }
1572
1573 guint32
1574 nm_ip6_config_get_num_nameservers (const NMIP6Config *config)
1575 {
1576         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1577
1578         return priv->nameservers->len;
1579 }
1580
1581 const struct in6_addr *
1582 nm_ip6_config_get_nameserver (const NMIP6Config *config, guint i)
1583 {
1584         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1585
1586         return &g_array_index (priv->nameservers, struct in6_addr, i);
1587 }
1588
1589 /******************************************************************/
1590
1591 void
1592 nm_ip6_config_reset_domains (NMIP6Config *config)
1593 {
1594         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1595
1596         if (priv->domains->len != 0) {
1597                 g_ptr_array_set_size (priv->domains, 0);
1598                 _notify (config, PROP_DOMAINS);
1599         }
1600 }
1601
1602 void
1603 nm_ip6_config_add_domain (NMIP6Config *config, const char *domain)
1604 {
1605         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1606         int i;
1607
1608         g_return_if_fail (domain != NULL);
1609         g_return_if_fail (domain[0] != '\0');
1610
1611         for (i = 0; i < priv->domains->len; i++)
1612                 if (!g_strcmp0 (g_ptr_array_index (priv->domains, i), domain))
1613                         return;
1614
1615         g_ptr_array_add (priv->domains, g_strdup (domain));
1616         _notify (config, PROP_DOMAINS);
1617 }
1618
1619 void
1620 nm_ip6_config_del_domain (NMIP6Config *config, guint i)
1621 {
1622         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1623
1624         g_return_if_fail (i < priv->domains->len);
1625
1626         g_ptr_array_remove_index (priv->domains, i);
1627         _notify (config, PROP_DOMAINS);
1628 }
1629
1630 guint32
1631 nm_ip6_config_get_num_domains (const NMIP6Config *config)
1632 {
1633         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1634
1635         return priv->domains->len;
1636 }
1637
1638 const char *
1639 nm_ip6_config_get_domain (const NMIP6Config *config, guint i)
1640 {
1641         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1642
1643         return g_ptr_array_index (priv->domains, i);
1644 }
1645
1646 /******************************************************************/
1647
1648 void
1649 nm_ip6_config_reset_searches (NMIP6Config *config)
1650 {
1651         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1652
1653         if (priv->searches->len != 0) {
1654                 g_ptr_array_set_size (priv->searches, 0);
1655                 _notify (config, PROP_SEARCHES);
1656         }
1657 }
1658
1659 void
1660 nm_ip6_config_add_search (NMIP6Config *config, const char *new)
1661 {
1662         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1663         char *search;
1664         size_t len;
1665
1666         g_return_if_fail (new != NULL);
1667         g_return_if_fail (new[0] != '\0');
1668
1669         search = g_strdup (new);
1670
1671         /* Remove trailing dot as it has no effect */
1672         len = strlen (search);
1673         if (search[len - 1] == '.')
1674                 search[len - 1] = 0;
1675
1676         if (!search[0]) {
1677                 g_free (search);
1678                 return;
1679         }
1680
1681         if (_nm_utils_strv_find_first ((char **) priv->searches->pdata,
1682                                        priv->searches->len, search) >= 0) {
1683                 g_free (search);
1684                 return;
1685         }
1686
1687         g_ptr_array_add (priv->searches, search);
1688         _notify (config, PROP_SEARCHES);
1689 }
1690
1691 void
1692 nm_ip6_config_del_search (NMIP6Config *config, guint i)
1693 {
1694         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1695
1696         g_return_if_fail (i < priv->searches->len);
1697
1698         g_ptr_array_remove_index (priv->searches, i);
1699         _notify (config, PROP_SEARCHES);
1700 }
1701
1702 guint32
1703 nm_ip6_config_get_num_searches (const NMIP6Config *config)
1704 {
1705         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1706
1707         return priv->searches->len;
1708 }
1709
1710 const char *
1711 nm_ip6_config_get_search (const NMIP6Config *config, guint i)
1712 {
1713         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1714
1715         return g_ptr_array_index (priv->searches, i);
1716 }
1717
1718 /******************************************************************/
1719
1720 void
1721 nm_ip6_config_reset_dns_options (NMIP6Config *config)
1722 {
1723         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1724
1725         if (priv->dns_options->len != 0) {
1726                 g_ptr_array_set_size (priv->dns_options, 0);
1727                 _notify (config, PROP_DNS_OPTIONS);
1728         }
1729 }
1730
1731 void
1732 nm_ip6_config_add_dns_option (NMIP6Config *config, const char *new)
1733 {
1734         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1735         int i;
1736
1737         g_return_if_fail (new != NULL);
1738         g_return_if_fail (new[0] != '\0');
1739
1740         for (i = 0; i < priv->dns_options->len; i++)
1741                 if (!g_strcmp0 (g_ptr_array_index (priv->dns_options, i), new))
1742                         return;
1743
1744         g_ptr_array_add (priv->dns_options, g_strdup (new));
1745         _notify (config, PROP_DNS_OPTIONS);
1746 }
1747
1748 void
1749 nm_ip6_config_del_dns_option (NMIP6Config *config, guint i)
1750 {
1751         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1752
1753         g_return_if_fail (i < priv->dns_options->len);
1754
1755         g_ptr_array_remove_index (priv->dns_options, i);
1756         _notify (config, PROP_DNS_OPTIONS);
1757 }
1758
1759 guint32
1760 nm_ip6_config_get_num_dns_options (const NMIP6Config *config)
1761 {
1762         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1763
1764         return priv->dns_options->len;
1765 }
1766
1767 const char *
1768 nm_ip6_config_get_dns_option (const NMIP6Config *config, guint i)
1769 {
1770         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1771
1772         return g_ptr_array_index (priv->dns_options, i);
1773 }
1774
1775 /******************************************************************/
1776
1777 void
1778 nm_ip6_config_set_mss (NMIP6Config *config, guint32 mss)
1779 {
1780         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1781
1782         priv->mss = mss;
1783 }
1784
1785 guint32
1786 nm_ip6_config_get_mss (const NMIP6Config *config)
1787 {
1788         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1789
1790         return priv->mss;
1791 }
1792
1793 /******************************************************************/
1794
1795 static inline void
1796 hash_u32 (GChecksum *sum, guint32 n)
1797 {
1798         g_checksum_update (sum, (const guint8 *) &n, sizeof (n));
1799 }
1800
1801 static inline void
1802 hash_in6addr (GChecksum *sum, const struct in6_addr *a)
1803 {
1804         if (a)
1805                 g_checksum_update (sum, (const guint8 *) a, sizeof (*a));
1806         else
1807                 g_checksum_update (sum, (const guint8 *) &in6addr_any, sizeof (in6addr_any));
1808 }
1809
1810 void
1811 nm_ip6_config_hash (const NMIP6Config *config, GChecksum *sum, gboolean dns_only)
1812 {
1813         guint32 i;
1814         const char *s;
1815
1816         g_return_if_fail (config);
1817         g_return_if_fail (sum);
1818
1819         if (dns_only == FALSE) {
1820                 hash_in6addr (sum, nm_ip6_config_get_gateway (config));
1821
1822                 for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++) {
1823                         const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
1824
1825                         hash_in6addr (sum, &address->address);
1826                         hash_u32 (sum, address->plen);
1827                 }
1828
1829                 for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) {
1830                         const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
1831
1832                         hash_in6addr (sum, &route->network);
1833                         hash_u32 (sum, route->plen);
1834                         hash_in6addr (sum, &route->gateway);
1835                         hash_u32 (sum, route->metric);
1836                 }
1837         }
1838
1839         for (i = 0; i < nm_ip6_config_get_num_nameservers (config); i++)
1840                 hash_in6addr (sum, nm_ip6_config_get_nameserver (config, i));
1841
1842         for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) {
1843                 s = nm_ip6_config_get_domain (config, i);
1844                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1845         }
1846
1847         for (i = 0; i < nm_ip6_config_get_num_searches (config); i++) {
1848                 s = nm_ip6_config_get_search (config, i);
1849                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1850         }
1851
1852         for (i = 0; i < nm_ip6_config_get_num_dns_options (config); i++) {
1853                 s = nm_ip6_config_get_dns_option (config, i);
1854                 g_checksum_update (sum, (const guint8 *) s, strlen (s));
1855         }
1856
1857 }
1858
1859 /**
1860  * nm_ip6_config_equal:
1861  * @a: first config to compare
1862  * @b: second config to compare
1863  *
1864  * Compares two #NMIP6Configs for basic equality.  This means that all
1865  * attributes must exist in the same order in both configs (addresses, routes,
1866  * domains, DNS servers, etc) but some attributes (address lifetimes, and address
1867  * and route sources) are ignored.
1868  *
1869  * Returns: %TRUE if the configurations are basically equal to each other,
1870  * %FALSE if not
1871  */
1872 gboolean
1873 nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b)
1874 {
1875         GChecksum *a_checksum = g_checksum_new (G_CHECKSUM_SHA1);
1876         GChecksum *b_checksum = g_checksum_new (G_CHECKSUM_SHA1);
1877         gsize a_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
1878         gsize b_len = g_checksum_type_get_length (G_CHECKSUM_SHA1);
1879         guchar a_data[a_len], b_data[b_len];
1880         gboolean equal;
1881
1882         if (a)
1883                 nm_ip6_config_hash (a, a_checksum, FALSE);
1884         if (b)
1885                 nm_ip6_config_hash (b, b_checksum, FALSE);
1886
1887         g_checksum_get_digest (a_checksum, a_data, &a_len);
1888         g_checksum_get_digest (b_checksum, b_data, &b_len);
1889
1890         g_assert (a_len == b_len);
1891         equal = !memcmp (a_data, b_data, a_len);
1892
1893         g_checksum_free (a_checksum);
1894         g_checksum_free (b_checksum);
1895
1896         return equal;
1897 }
1898
1899 /******************************************************************/
1900
1901 static void
1902 nm_ip6_config_init (NMIP6Config *config)
1903 {
1904         NMIP6ConfigPrivate *priv;
1905
1906         priv = G_TYPE_INSTANCE_GET_PRIVATE (config, NM_TYPE_IP6_CONFIG, NMIP6ConfigPrivate);
1907         config->priv = priv;
1908
1909         priv->addresses = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Address));
1910         priv->routes = g_array_new (FALSE, TRUE, sizeof (NMPlatformIP6Route));
1911         priv->nameservers = g_array_new (FALSE, TRUE, sizeof (struct in6_addr));
1912         priv->domains = g_ptr_array_new_with_free_func (g_free);
1913         priv->searches = g_ptr_array_new_with_free_func (g_free);
1914         priv->dns_options = g_ptr_array_new_with_free_func (g_free);
1915         priv->route_metric = -1;
1916 }
1917
1918 static void
1919 finalize (GObject *object)
1920 {
1921         NMIP6Config *self = NM_IP6_CONFIG (object);
1922         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (self);
1923
1924         g_array_unref (priv->addresses);
1925         g_array_unref (priv->routes);
1926         g_array_unref (priv->nameservers);
1927         g_ptr_array_unref (priv->domains);
1928         g_ptr_array_unref (priv->searches);
1929         g_ptr_array_unref (priv->dns_options);
1930
1931         G_OBJECT_CLASS (nm_ip6_config_parent_class)->finalize (object);
1932 }
1933
1934 static void
1935 nameservers_to_gvalue (GArray *array, GValue *value)
1936 {
1937         GVariantBuilder builder;
1938         guint i = 0;
1939
1940         g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
1941
1942         while (array && (i < array->len)) {
1943                 struct in6_addr *addr;
1944
1945                 addr = &g_array_index (array, struct in6_addr, i++);
1946                 g_variant_builder_add (&builder, "@ay",
1947                                        g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
1948                                                                   addr, 16, 1));
1949         }
1950
1951         g_value_take_variant (value, g_variant_builder_end (&builder));
1952 }
1953
1954 static void
1955 get_property (GObject *object, guint prop_id,
1956                           GValue *value, GParamSpec *pspec)
1957 {
1958         NMIP6Config *config = NM_IP6_CONFIG (object);
1959         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
1960
1961         switch (prop_id) {
1962         case PROP_IFINDEX:
1963                 g_value_set_int (value, priv->ifindex);
1964                 break;
1965         case PROP_ADDRESS_DATA:
1966                 {
1967                         GVariantBuilder array_builder, addr_builder;
1968                         int naddr = nm_ip6_config_get_num_addresses (config);
1969                         int i;
1970
1971                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
1972                         for (i = 0; i < naddr; i++) {
1973                                 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
1974
1975                                 g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
1976                                 g_variant_builder_add (&addr_builder, "{sv}",
1977                                                        "address",
1978                                                        g_variant_new_string (nm_utils_inet6_ntop (&address->address, NULL)));
1979                                 g_variant_builder_add (&addr_builder, "{sv}",
1980                                                        "prefix",
1981                                                        g_variant_new_uint32 (address->plen));
1982                                 if (   !IN6_IS_ADDR_UNSPECIFIED (&address->peer_address)
1983                                     && !IN6_ARE_ADDR_EQUAL (&address->peer_address, &address->address)) {
1984                                         g_variant_builder_add (&addr_builder, "{sv}",
1985                                                                "peer",
1986                                                                g_variant_new_string (nm_utils_inet6_ntop (&address->peer_address, NULL)));
1987                                 }
1988
1989                                 g_variant_builder_add (&array_builder, "a{sv}", &addr_builder);
1990                         }
1991
1992                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
1993                 }
1994                 break;
1995         case PROP_ADDRESSES:
1996                 {
1997                         GVariantBuilder array_builder;
1998                         const struct in6_addr *gateway = nm_ip6_config_get_gateway (config);
1999                         int naddr = nm_ip6_config_get_num_addresses (config);
2000                         int i;
2001
2002                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuay)"));
2003                         for (i = 0; i < naddr; i++) {
2004                                 const NMPlatformIP6Address *address = nm_ip6_config_get_address (config, i);
2005
2006                                 g_variant_builder_add (&array_builder, "(@ayu@ay)",
2007                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2008                                                                                   &address->address, 16, 1),
2009                                                        address->plen,
2010                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2011                                                                                   (i == 0 && gateway ? gateway : &in6addr_any),
2012                                                                                   16, 1));
2013                         }
2014
2015                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2016                 }
2017                 break;
2018         case PROP_ROUTE_DATA:
2019                 {
2020                         GVariantBuilder array_builder, route_builder;
2021                         guint nroutes = nm_ip6_config_get_num_routes (config);
2022                         int i;
2023
2024                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("aa{sv}"));
2025                         for (i = 0; i < nroutes; i++) {
2026                                 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
2027
2028                                 g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
2029                                 g_variant_builder_add (&route_builder, "{sv}",
2030                                                        "dest",
2031                                                        g_variant_new_string (nm_utils_inet6_ntop (&route->network, NULL)));
2032                                 g_variant_builder_add (&route_builder, "{sv}",
2033                                                        "prefix",
2034                                                        g_variant_new_uint32 (route->plen));
2035                                 if (!IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
2036                                         g_variant_builder_add (&route_builder, "{sv}",
2037                                                                "next-hop",
2038                                                                g_variant_new_string (nm_utils_inet6_ntop (&route->gateway, NULL)));
2039                                 }
2040
2041                                 g_variant_builder_add (&route_builder, "{sv}",
2042                                                        "metric",
2043                                                        g_variant_new_uint32 (route->metric));
2044
2045                                 g_variant_builder_add (&array_builder, "a{sv}", &route_builder);
2046                         }
2047
2048                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2049                 }
2050                 break;
2051         case PROP_ROUTES:
2052                 {
2053                         GVariantBuilder array_builder;
2054                         int nroutes = nm_ip6_config_get_num_routes (config);
2055                         int i;
2056
2057                         g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a(ayuayu)"));
2058                         for (i = 0; i < nroutes; i++) {
2059                                 const NMPlatformIP6Route *route = nm_ip6_config_get_route (config, i);
2060
2061                                 /* legacy versions of nm_ip6_route_set_prefix() in libnm-util assert that the
2062                                  * plen is positive. Skip the default routes not to break older clients. */
2063                                 if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route))
2064                                         continue;
2065
2066                                 g_variant_builder_add (&array_builder, "(@ayu@ayu)",
2067                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2068                                                                                   &route->network, 16, 1),
2069                                                        (guint32) route->plen,
2070                                                        g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
2071                                                                                   &route->gateway, 16, 1),
2072                                                        (guint32) route->metric);
2073                         }
2074
2075                         g_value_take_variant (value, g_variant_builder_end (&array_builder));
2076                 }
2077                 break;
2078         case PROP_GATEWAY:
2079                 if (!IN6_IS_ADDR_UNSPECIFIED (&priv->gateway))
2080                         g_value_set_string (value, nm_utils_inet6_ntop (&priv->gateway, NULL));
2081                 else
2082                         g_value_set_string (value, NULL);
2083                 break;
2084         case PROP_NAMESERVERS:
2085                 nameservers_to_gvalue (priv->nameservers, value);
2086                 break;
2087         case PROP_DOMAINS:
2088                 nm_utils_g_value_set_strv (value, priv->domains);
2089                 break;
2090         case PROP_SEARCHES:
2091                 nm_utils_g_value_set_strv (value, priv->searches);
2092                 break;
2093         case PROP_DNS_OPTIONS:
2094                 nm_utils_g_value_set_strv (value, priv->dns_options);
2095                 break;
2096         default:
2097                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2098                 break;
2099         }
2100 }
2101
2102 static void
2103 set_property (GObject *object,
2104               guint prop_id,
2105               const GValue *value,
2106               GParamSpec *pspec)
2107 {
2108         NMIP6Config *config = NM_IP6_CONFIG (object);
2109         NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
2110
2111         switch (prop_id) {
2112         case PROP_IFINDEX:
2113                 priv->ifindex = g_value_get_int (value);
2114                 break;
2115         default:
2116                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2117                 break;
2118         }
2119 }
2120
2121 static void
2122 nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
2123 {
2124         GObjectClass *object_class = G_OBJECT_CLASS (config_class);
2125         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (config_class);
2126
2127         g_type_class_add_private (config_class, sizeof (NMIP6ConfigPrivate));
2128
2129         exported_object_class->export_path = NM_DBUS_PATH "/IP6Config/%u";
2130
2131         /* virtual methods */
2132         object_class->get_property = get_property;
2133         object_class->set_property = set_property;
2134         object_class->finalize = finalize;
2135
2136         /* properties */
2137         obj_properties[PROP_IFINDEX] =
2138                 g_param_spec_int (NM_IP6_CONFIG_IFINDEX, "", "",
2139                                   -1, G_MAXINT, -1,
2140                                   G_PARAM_READWRITE |
2141                                   G_PARAM_CONSTRUCT_ONLY |
2142                                   G_PARAM_STATIC_STRINGS);
2143         obj_properties[PROP_ADDRESS_DATA] =
2144                 g_param_spec_variant (NM_IP6_CONFIG_ADDRESS_DATA, "", "",
2145                                       G_VARIANT_TYPE ("aa{sv}"),
2146                                       NULL,
2147                                       G_PARAM_READABLE |
2148                                       G_PARAM_STATIC_STRINGS);
2149         obj_properties[PROP_ADDRESSES] =
2150                 g_param_spec_variant (NM_IP6_CONFIG_ADDRESSES, "", "",
2151                                       G_VARIANT_TYPE ("a(ayuay)"),
2152                                       NULL,
2153                                       G_PARAM_READABLE |
2154                                       G_PARAM_STATIC_STRINGS);
2155         obj_properties[PROP_ROUTE_DATA] =
2156                 g_param_spec_variant (NM_IP6_CONFIG_ROUTE_DATA, "", "",
2157                                       G_VARIANT_TYPE ("aa{sv}"),
2158                                       NULL,
2159                                       G_PARAM_READABLE |
2160                                       G_PARAM_STATIC_STRINGS);
2161         obj_properties[PROP_ROUTES] =
2162                 g_param_spec_variant (NM_IP6_CONFIG_ROUTES, "", "",
2163                                       G_VARIANT_TYPE ("a(ayuayu)"),
2164                                       NULL,
2165                                       G_PARAM_READABLE |
2166                                       G_PARAM_STATIC_STRINGS);
2167         obj_properties[PROP_GATEWAY] =
2168                 g_param_spec_string (NM_IP6_CONFIG_GATEWAY, "", "",
2169                                      NULL,
2170                                      G_PARAM_READABLE |
2171                                      G_PARAM_STATIC_STRINGS);
2172         obj_properties[PROP_NAMESERVERS] =
2173                 g_param_spec_variant (NM_IP6_CONFIG_NAMESERVERS, "", "",
2174                                       G_VARIANT_TYPE ("aay"),
2175                                       NULL,
2176                                       G_PARAM_READABLE |
2177                                       G_PARAM_STATIC_STRINGS);
2178         obj_properties[PROP_DOMAINS] =
2179                 g_param_spec_boxed (NM_IP6_CONFIG_DOMAINS, "", "",
2180                                     G_TYPE_STRV,
2181                                     G_PARAM_READABLE |
2182                                     G_PARAM_STATIC_STRINGS);
2183         obj_properties[PROP_SEARCHES] =
2184                 g_param_spec_boxed (NM_IP6_CONFIG_SEARCHES, "", "",
2185                                     G_TYPE_STRV,
2186                                     G_PARAM_READABLE |
2187                                     G_PARAM_STATIC_STRINGS);
2188         obj_properties[PROP_DNS_OPTIONS] =
2189                 g_param_spec_boxed (NM_IP6_CONFIG_DNS_OPTIONS, "", "",
2190                                     G_TYPE_STRV,
2191                                     G_PARAM_READABLE |
2192                                     G_PARAM_STATIC_STRINGS);
2193
2194         g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
2195
2196         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (config_class),
2197                                                 NMDBUS_TYPE_IP6_CONFIG_SKELETON,
2198                                                 NULL);
2199 }