4a5dee587d3dc9ba48eb21314d9d0b7b9d6dd32c
[NetworkManager.git] / src / ip6-manager / nm-ip6-manager.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* nm-ip6-manager.c - Handle IPv6 address configuration for NetworkManager
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, or (at your option)
7  * 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) 2009 - 2010 Red Hat, Inc.
19  */
20
21 #include <errno.h>
22 #include <netinet/icmp6.h>
23
24 #include <netlink/route/addr.h>
25 #include <netlink/route/rtnl.h>
26 #include <netlink/route/route.h>
27
28 #include "nm-ip6-manager.h"
29 #include "nm-netlink-monitor.h"
30 #include "NetworkManagerUtils.h"
31 #include "nm-marshal.h"
32 #include "nm-logging.h"
33
34 /* Pre-DHCP addrconf timeout, in seconds */
35 #define NM_IP6_TIMEOUT 20
36
37 /* FIXME? Stolen from the kernel sources */
38 #define IF_RA_OTHERCONF 0x80
39 #define IF_RA_MANAGED   0x40
40 #define IF_RA_RCVD      0x20
41 #define IF_RS_SENT      0x10
42
43 typedef struct {
44         NMNetlinkMonitor *monitor;
45         GHashTable *devices;
46
47         struct nl_handle *nlh;
48         struct nl_cache *addr_cache, *route_cache;
49
50         guint netlink_id;
51 } NMIP6ManagerPrivate;
52
53 #define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate))
54
55 G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT)
56
57 enum {
58         ADDRCONF_COMPLETE,
59         CONFIG_CHANGED,
60         LAST_SIGNAL
61 };
62
63 static guint signals[LAST_SIGNAL] = { 0 };
64
65 typedef enum {
66         NM_IP6_DEVICE_UNCONFIGURED,
67         NM_IP6_DEVICE_GOT_LINK_LOCAL,
68         NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT,
69         NM_IP6_DEVICE_GOT_ADDRESS,
70         NM_IP6_DEVICE_TIMED_OUT
71 } NMIP6DeviceState;
72
73 typedef struct {
74         struct in6_addr addr;
75         time_t expires;
76 } NMIP6RDNSS;
77
78 typedef struct {
79         char domain[256];
80         time_t expires;
81 } NMIP6DNSSL;
82
83 /******************************************************************/
84
85 typedef struct {
86         NMIP6Manager *manager;
87         char *iface;
88         int ifindex;
89
90         char *disable_ip6_path;
91         gboolean disable_ip6_save_valid;
92         guint32 disable_ip6_save;
93
94         guint finish_addrconf_id;
95         guint config_changed_id;
96
97         NMIP6DeviceState state;
98         NMIP6DeviceState target_state;
99         gboolean addrconf_complete;
100
101         GArray *rdnss_servers;
102         guint rdnss_timeout_id;
103
104         GArray *dnssl_domains;
105         guint dnssl_timeout_id;
106
107         guint ip6flags_poll_id;
108
109         guint32 ra_flags;
110 } NMIP6Device;
111
112 static void
113 nm_ip6_device_destroy (NMIP6Device *device)
114 {
115         g_return_if_fail (device != NULL);
116
117         /* reset the saved IPv6 value */
118         if (device->disable_ip6_save_valid) {
119                 nm_utils_do_sysctl (device->disable_ip6_path,
120                                     device->disable_ip6_save ? "1\n" : "0\n");
121         }
122
123         if (device->finish_addrconf_id)
124                 g_source_remove (device->finish_addrconf_id);
125         if (device->config_changed_id)
126                 g_source_remove (device->config_changed_id);
127         g_free (device->iface);
128         if (device->rdnss_servers)
129                 g_array_free (device->rdnss_servers, TRUE);
130         if (device->rdnss_timeout_id)
131                 g_source_remove (device->rdnss_timeout_id);
132         if (device->dnssl_domains)
133                 g_array_free (device->dnssl_domains, TRUE);
134         if (device->dnssl_timeout_id)
135                 g_source_remove (device->dnssl_timeout_id);
136         if (device->ip6flags_poll_id)
137                 g_source_remove (device->ip6flags_poll_id);
138
139         g_slice_free (NMIP6Device, device);
140 }
141
142 static NMIP6Device *
143 nm_ip6_device_new (NMIP6Manager *manager, int ifindex)
144 {
145         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
146         NMIP6Device *device;
147
148         g_return_val_if_fail (ifindex > 0, NULL);
149
150         device = g_slice_new0 (NMIP6Device);
151         if (!device) {
152                 nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.",
153                             ifindex);
154                 return NULL;
155         }
156
157         device->ifindex = ifindex;
158         device->iface = g_strdup (nm_netlink_index_to_iface (ifindex));
159         if (!device->iface) {
160                 nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.",
161                             ifindex);
162                 goto error;
163         }
164
165         device->manager = manager;
166
167         device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
168
169         device->dnssl_domains = g_array_new (FALSE, FALSE, sizeof (NMIP6DNSSL));
170
171         g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device);
172
173         /* and the original value of IPv6 enable/disable */
174         device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6",
175                                                     device->iface);
176         g_assert (device->disable_ip6_path);
177         device->disable_ip6_save_valid = nm_utils_get_proc_sys_net_value (device->disable_ip6_path,
178                                                                           device->iface,
179                                                                           &device->disable_ip6_save);
180
181         return device;
182
183 error:
184         nm_ip6_device_destroy (device);
185         return NULL;
186 }
187
188 static NMIP6Device *
189 nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
190 {
191         NMIP6ManagerPrivate *priv;
192
193         g_return_val_if_fail (manager != NULL, NULL);
194         g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
195
196         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
197         return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
198 }
199
200 /******************************************************************/
201
202 typedef struct {
203         NMIP6Device *device;
204         guint dhcp_opts;
205         gboolean success;
206 } CallbackInfo;
207
208 static gboolean
209 finish_addrconf (gpointer user_data)
210 {
211         CallbackInfo *info = user_data;
212         NMIP6Device *device = info->device;
213         NMIP6Manager *manager = device->manager;
214         int ifindex;
215
216         device->finish_addrconf_id = 0;
217         device->addrconf_complete = TRUE;
218         ifindex = device->ifindex;
219
220         /* We're done, stop polling IPv6 flags */
221         if (device->ip6flags_poll_id) {
222                 g_source_remove (device->ip6flags_poll_id);
223                 device->ip6flags_poll_id = 0;
224         }
225
226         /* And tell listeners that addrconf is complete */
227         if (info->success) {
228                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
229                                ifindex, info->dhcp_opts, TRUE);
230         } else {
231                 nm_log_info (LOGD_IP6, "(%s): IP6 addrconf timed out or failed.",
232                              device->iface);
233
234                 nm_ip6_manager_cancel_addrconf (manager, ifindex);
235                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
236                                ifindex, info->dhcp_opts, FALSE);
237         }
238
239         return FALSE;
240 }
241
242 static gboolean
243 emit_config_changed (gpointer user_data)
244 {
245         CallbackInfo *info = user_data;
246         NMIP6Device *device = info->device;
247         NMIP6Manager *manager = device->manager;
248
249         device->config_changed_id = 0;
250         g_signal_emit (manager, signals[CONFIG_CHANGED], 0,
251                        device->ifindex,
252                        info->dhcp_opts,
253                        info->success);
254         return FALSE;
255 }
256
257 static void set_rdnss_timeout (NMIP6Device *device);
258
259 static gboolean
260 rdnss_expired (gpointer user_data)
261 {
262         NMIP6Device *device = user_data;
263         CallbackInfo info = { device, IP6_DHCP_OPT_NONE, FALSE };
264
265         nm_log_dbg (LOGD_IP6, "(%s): IPv6 RDNSS information expired", device->iface);
266
267         set_rdnss_timeout (device);
268         emit_config_changed (&info);
269         return FALSE;
270 }
271
272 static void
273 set_rdnss_timeout (NMIP6Device *device)
274 {
275         time_t expires = 0, now = time (NULL);
276         NMIP6RDNSS *rdnss;
277         int i;
278
279         if (device->rdnss_timeout_id) {
280                 g_source_remove (device->rdnss_timeout_id);
281                 device->rdnss_timeout_id = 0;
282         }
283
284         /* Find the soonest expiration time. */
285         for (i = 0; i < device->rdnss_servers->len; i++) {
286                 rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i);
287                 if (rdnss->expires == 0)
288                         continue;
289
290                 /* If the entry has already expired, remove it; the "+ 1" is
291                  * because g_timeout_add_seconds() might fudge the timing a
292                  * bit.
293                  */
294                 if (rdnss->expires <= now + 1) {
295                         char buf[INET6_ADDRSTRLEN + 1];
296
297                         if (inet_ntop (AF_INET6, &(rdnss->addr), buf, sizeof (buf)) > 0) {
298                                 nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided nameserver %s",
299                                             device->iface, buf);
300                         }
301                         g_array_remove_index (device->rdnss_servers, i--);
302                         continue;
303                 }
304
305                 if (!expires || rdnss->expires < expires)
306                         expires = rdnss->expires;
307         }
308
309         if (expires) {
310                 device->rdnss_timeout_id = g_timeout_add_seconds (MIN (expires - now, G_MAXUINT32 - 1),
311                                                                   rdnss_expired,
312                                                                   device);
313         }
314 }
315
316 static void set_dnssl_timeout (NMIP6Device *device);
317
318 static gboolean
319 dnssl_expired (gpointer user_data)
320 {
321         NMIP6Device *device = user_data;
322         CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
323
324         nm_log_dbg (LOGD_IP6, "(%s): IPv6 DNSSL information expired", device->iface);
325
326         set_dnssl_timeout (device);
327         emit_config_changed (&info);
328         return FALSE;
329 }
330
331 static void
332 set_dnssl_timeout (NMIP6Device *device)
333 {
334         time_t expires = 0, now = time (NULL);
335         NMIP6DNSSL *dnssl;
336         int i;
337
338         if (device->dnssl_timeout_id) {
339                 g_source_remove (device->dnssl_timeout_id);
340                 device->dnssl_timeout_id = 0;
341         }
342
343         /* Find the soonest expiration time. */
344         for (i = 0; i < device->dnssl_domains->len; i++) {
345                 dnssl = &g_array_index (device->dnssl_domains, NMIP6DNSSL, i);
346                 if (dnssl->expires == 0)
347                         continue;
348
349                 /* If the entry has already expired, remove it; the "+ 1" is
350                  * because g_timeout_add_seconds() might fudge the timing a
351                  * bit.
352                  */
353                 if (dnssl->expires <= now + 1) {
354                         nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided domain %s",
355                                     device->iface, dnssl->domain);
356                         g_array_remove_index (device->dnssl_domains, i--);
357                         continue;
358                 }
359
360                 if (!expires || dnssl->expires < expires)
361                         expires = dnssl->expires;
362         }
363
364         if (expires) {
365                 device->dnssl_timeout_id = g_timeout_add_seconds (MIN (expires - now, G_MAXUINT32 - 1),
366                                                                   dnssl_expired,
367                                                                   device);
368         }
369 }
370
371 static CallbackInfo *
372 callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success)
373 {
374         CallbackInfo *info;
375
376         info = g_malloc0 (sizeof (CallbackInfo));
377         info->device = device;
378         info->dhcp_opts = dhcp_opts;
379         info->success = success;
380         return info;
381 }
382
383 static const char *
384 state_to_string (NMIP6DeviceState state)
385 {
386         switch (state) {
387         case NM_IP6_DEVICE_UNCONFIGURED:
388                 return "unconfigured";
389         case NM_IP6_DEVICE_GOT_LINK_LOCAL:
390                 return "got-link-local";
391         case NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT:
392                 return "got-ra";
393         case NM_IP6_DEVICE_GOT_ADDRESS:
394                 return "got-address";
395         case NM_IP6_DEVICE_TIMED_OUT:
396                 return "timed-out";
397         default:
398                 return "unknown";
399         }
400 }
401
402 static void
403 nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
404 {
405         NMIP6Manager *manager = device->manager;
406         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
407         struct rtnl_addr *rtnladdr;
408         struct nl_addr *nladdr;
409         struct in6_addr *addr;
410         CallbackInfo *info;
411         guint dhcp_opts = IP6_DHCP_OPT_NONE;
412         gboolean found_linklocal = FALSE, found_other = FALSE;
413
414         nm_log_dbg (LOGD_IP6, "(%s): syncing with netlink (ra_flags 0x%X) (state/target '%s'/'%s')",
415                     device->iface, device->ra_flags,
416                     state_to_string (device->state),
417                     state_to_string (device->target_state));
418
419         /* Look for any IPv6 addresses the kernel may have set for the device */
420         for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache);
421                  rtnladdr;
422                  rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) {
423                 char buf[INET6_ADDRSTRLEN];
424
425                 if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
426                         continue;
427
428                 nladdr = rtnl_addr_get_local (rtnladdr);
429                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
430                         continue;
431
432                 addr = nl_addr_get_binary_addr (nladdr);
433
434                 if (inet_ntop (AF_INET6, addr, buf, INET6_ADDRSTRLEN) > 0) {
435                         nm_log_dbg (LOGD_IP6, "(%s): netlink address: %s",
436                                     device->iface, buf);
437                 }
438
439                 if (IN6_IS_ADDR_LINKLOCAL (addr)) {
440                         if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
441                                 device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
442                         found_linklocal = TRUE;
443                 } else {
444                         if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
445                                 device->state = NM_IP6_DEVICE_GOT_ADDRESS;
446                         found_other = TRUE;
447                 }
448         }
449
450         /* There might be a LL address hanging around on the interface from
451          * before in the initial run, but if it goes away later, make sure we
452          * regress from GOT_LINK_LOCAL back to UNCONFIGURED.
453          */
454         if ((device->state == NM_IP6_DEVICE_GOT_LINK_LOCAL) && !found_linklocal)
455                 device->state = NM_IP6_DEVICE_UNCONFIGURED;
456
457         nm_log_dbg (LOGD_IP6, "(%s): addresses synced (state %s)",
458                     device->iface, state_to_string (device->state));
459
460         /* We only care about router advertisements if we want a real IPv6 address */
461         if (   (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS)
462             && (device->ra_flags & IF_RA_RCVD)) {
463
464                 if (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
465                         device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
466
467                 if (device->ra_flags & IF_RA_MANAGED) {
468                         dhcp_opts = IP6_DHCP_OPT_MANAGED;
469                         nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6");
470                 } else if (device->ra_flags & IF_RA_OTHERCONF) {
471                         dhcp_opts = IP6_DHCP_OPT_OTHERCONF;
472                         nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6");
473                 }
474         }
475
476         if (!device->addrconf_complete) {
477                 /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so
478                  * we don't bother waiting for the device's target state to be reached
479                  * when the RA requests managed mode.
480                  */
481                 if (   (device->state >= device->target_state)
482                     || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) {
483                         /* device->finish_addrconf_id may currently be a timeout
484                          * rather than an idle, so we remove the existing source.
485                          */
486                         if (device->finish_addrconf_id)
487                                 g_source_remove (device->finish_addrconf_id);
488
489                         nm_log_dbg (LOGD_IP6, "(%s): reached target state or Managed-mode requested (state '%s') (dhcp opts 0x%X)",
490                                     device->iface, state_to_string (device->state),
491                                     dhcp_opts);
492
493                         info = callback_info_new (device, dhcp_opts, TRUE);
494                         device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
495                                                                       finish_addrconf,
496                                                                       info,
497                                                                       (GDestroyNotify) g_free);
498                 }
499         } else if (config_changed) {
500                 if (!device->config_changed_id) {
501                         gboolean success = TRUE;
502
503                         /* If for some reason an RA-provided address disappeared, we need
504                          * to make sure we fail the connection as it's no longer valid.
505                          */
506                         if (   (device->state == NM_IP6_DEVICE_GOT_ADDRESS)
507                             && (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS)
508                             && !found_other) {
509                                 nm_log_dbg (LOGD_IP6, "(%s): RA-provided address no longer valid",
510                                             device->iface);
511                                 success = FALSE;
512                         }
513
514                         info = callback_info_new (device, dhcp_opts, success);
515                         device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
516                                                                      emit_config_changed,
517                                                                      info,
518                                                                      (GDestroyNotify) g_free);
519                 }
520         }
521 }
522
523 static void
524 ref_object (struct nl_object *obj, void *data)
525 {
526         struct nl_object **out = data;
527
528         nl_object_get (obj);
529         *out = obj;
530 }
531
532 static NMIP6Device *
533 process_addr (NMIP6Manager *manager, struct nl_msg *msg)
534 {
535         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
536         NMIP6Device *device;
537         struct rtnl_addr *rtnladdr;
538         int old_size;
539
540         nm_log_dbg (LOGD_IP6, "processing netlink new/del address message");
541
542         rtnladdr = NULL;
543         nl_msg_parse (msg, ref_object, &rtnladdr);
544         if (!rtnladdr) {
545                 nm_log_dbg (LOGD_IP6, "error processing netlink new/del address message");
546                 return NULL;
547         }
548
549         device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));
550         if (!device) {
551                 nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
552                 return NULL;
553         }
554
555         old_size = nl_cache_nitems (priv->addr_cache);
556         nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL);
557         rtnl_addr_put (rtnladdr);
558
559         /* The kernel will re-notify us of automatically-added addresses
560          * every time it gets another router advertisement. We only want
561          * to notify higher levels if we actually changed something.
562          */
563         if (nl_cache_nitems (priv->addr_cache) == old_size) {
564                 nm_log_dbg (LOGD_IP6, "(%s): address cache unchanged, ignoring message",
565                             device->iface);
566                 return NULL;
567         }
568
569         return device;
570 }
571
572 static NMIP6Device *
573 process_route (NMIP6Manager *manager, struct nl_msg *msg)
574 {
575         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
576         NMIP6Device *device;
577         struct rtnl_route *rtnlroute;
578         int old_size;
579
580         nm_log_dbg (LOGD_IP6, "processing netlink new/del route message");
581
582         rtnlroute = NULL;
583         nl_msg_parse (msg, ref_object, &rtnlroute);
584         if (!rtnlroute) {
585                 nm_log_dbg (LOGD_IP6, "error processing netlink new/del route message");
586                 return NULL;
587         }
588
589         device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
590         if (!device) {
591                 nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
592                 return NULL;
593         }
594
595         old_size = nl_cache_nitems (priv->route_cache);
596         nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL);
597         rtnl_route_put (rtnlroute);
598
599         /* As above in process_addr */
600         if (nl_cache_nitems (priv->route_cache) == old_size) {
601                 nm_log_dbg (LOGD_IP6, "(%s): route cache unchanged, ignoring message",
602                             device->iface);
603                 return NULL;
604         }
605
606         return device;
607 }
608
609 static NMIP6Device *
610 process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
611 {
612         struct prefixmsg *pmsg;
613         NMIP6Device *device;
614
615         /* We don't care about the prefix itself, but if we receive a
616          * router advertisement telling us to use DHCP, we might not
617          * get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only
618          * way to notice immediately that an RA was received.
619          */
620
621         nm_log_dbg (LOGD_IP6, "processing netlink new prefix message");
622
623         pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg));
624         device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex);
625
626         if (!device || device->addrconf_complete) {
627                 nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device",
628                             device ? device->iface : "(none)");
629                 return NULL;
630         }
631
632         return device;
633 }
634
635 /* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard,
636  * RĂ©mi Denis-Courmont. GPLv2/3
637  */
638
639 #define ND_OPT_RDNSS 25
640 #define ND_OPT_DNSSL 31
641
642 struct nd_opt_rdnss {
643         uint8_t nd_opt_rdnss_type;
644         uint8_t nd_opt_rdnss_len;
645         uint16_t nd_opt_rdnss_reserved1;
646         uint32_t nd_opt_rdnss_lifetime;
647         /* followed by one or more IPv6 addresses */
648 } __attribute__ ((packed));
649
650 struct nd_opt_dnssl {
651         uint8_t nd_opt_dnssl_type;
652         uint8_t nd_opt_dnssl_len;
653         uint16_t nd_opt_dnssl_reserved1;
654         uint32_t nd_opt_dnssl_lifetime;
655         /* followed by one or more suffixes */
656 } __attribute__ ((packed));
657
658 static gboolean
659 process_nduseropt_rdnss (NMIP6Device *device, struct nd_opt_hdr *opt)
660 {
661         size_t opt_len;
662         struct nd_opt_rdnss *rdnss_opt;
663         time_t now = time (NULL);
664         struct in6_addr *addr;
665         GArray *new_servers;
666         NMIP6RDNSS server, *cur_server;
667         gboolean changed = FALSE;
668         guint i;
669
670         opt_len = opt->nd_opt_len;
671
672         if (opt_len < 3 || (opt_len & 1) == 0)
673                 return FALSE;
674
675         rdnss_opt = (struct nd_opt_rdnss *) opt;
676
677         new_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
678
679         /* Pad the DNS server expiry somewhat to give a bit of slack in cases
680          * where one RA gets lost or something (which can happen on unreliable
681          * links like WiFi where certain types of frames are not retransmitted).
682          * Note that 0 has special meaning and is therefore not adjusted.
683          */
684         server.expires = ntohl (rdnss_opt->nd_opt_rdnss_lifetime);
685         if (server.expires > 0)
686                 server.expires += now + 10;
687
688         for (addr = (struct in6_addr *) (rdnss_opt + 1); opt_len >= 2; addr++, opt_len -= 2) {
689                 char buf[INET6_ADDRSTRLEN + 1];
690
691                 if (!inet_ntop (AF_INET6, addr, buf, sizeof (buf))) {
692                         nm_log_warn (LOGD_IP6, "(%s): received invalid RA-provided nameserver", device->iface);
693                         continue;
694                 }
695
696                 /* Update the cached timeout if we already saw this server */
697                 for (i = 0; i < device->rdnss_servers->len; i++) {
698                         cur_server = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
699
700                         if (!IN6_ARE_ADDR_EQUAL (addr, &cur_server->addr))
701                                 continue;
702
703                         cur_server->expires = server.expires;
704
705                         if (server.expires > 0) {
706                                 nm_log_dbg (LOGD_IP6, "(%s): refreshing RA-provided nameserver %s (expires in %ld seconds)",
707                                             device->iface, buf,
708                                             server.expires - now);
709                                 break;
710                         }
711
712                         nm_log_dbg (LOGD_IP6, "(%s): removing RA-provided nameserver %s on router request",
713                                     device->iface, buf);
714
715                         g_array_remove_index (device->rdnss_servers, i);
716                         changed = TRUE;
717                         break;
718                 }
719
720                 if (server.expires == 0)
721                         continue;
722                 if (i < device->rdnss_servers->len)
723                         continue;
724
725                 nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %ld seconds)",
726                             device->iface, buf, server.expires - now);
727
728                 server.addr = *addr;
729                 g_array_append_val (new_servers, server);
730         }
731
732         /* New servers must be added in the order they are listed in the
733          * RA option and before any existing servers.
734          *
735          * Note: This is the place to remove servers if we want to cap the
736          *       number of resolvers. The RFC states that the one to expire
737          *       first of the existing servers should be removed.
738          */
739         if (new_servers->len) {
740                 g_array_prepend_vals (device->rdnss_servers,
741                                       new_servers->data, new_servers->len);
742                 changed = TRUE;
743         }
744
745         g_array_free (new_servers, TRUE);
746
747         /* Timeouts may have changed even if IPs didn't */
748         set_rdnss_timeout (device);
749
750         return changed;
751 }
752
753 static const char *
754 parse_dnssl_domain (const unsigned char *buffer, size_t maxlen)
755 {
756         static char domain[256];
757         size_t label_len;
758
759         domain[0] = '\0';
760
761         while (maxlen > 0) {
762                 label_len = *buffer;
763                 buffer++;
764                 maxlen--;
765
766                 if (label_len == 0)
767                         return domain;
768
769                 if (label_len > maxlen)
770                         return NULL;
771                 if ((sizeof (domain) - strlen (domain)) < (label_len + 2))
772                         return NULL;
773
774                 if (domain[0] != '\0')
775                         strcat (domain, ".");
776                 strncat (domain, (const char *)buffer, label_len);
777                 buffer += label_len;
778                 maxlen -= label_len;
779         }
780
781         return NULL;
782 }
783
784 static gboolean
785 process_nduseropt_dnssl (NMIP6Device *device, struct nd_opt_hdr *opt)
786 {
787         size_t opt_len;
788         struct nd_opt_dnssl *dnssl_opt;
789         unsigned char *opt_ptr;
790         time_t now = time (NULL);
791         GArray *new_domains;
792         NMIP6DNSSL domain, *cur_domain;
793         gboolean changed;
794         guint i;
795
796         opt_len = opt->nd_opt_len;
797
798         if (opt_len < 2)
799                 return FALSE;
800
801         dnssl_opt = (struct nd_opt_dnssl *) opt;
802
803         opt_ptr = (unsigned char *)(dnssl_opt + 1);
804         opt_len = (opt_len - 1) * 8; /* prefer bytes for later handling */
805
806         new_domains = g_array_new (FALSE, FALSE, sizeof (NMIP6DNSSL));
807
808         changed = FALSE;
809
810         /* Pad the DNS server expiry somewhat to give a bit of slack in cases
811          * where one RA gets lost or something (which can happen on unreliable
812          * links like wifi where certain types of frames are not retransmitted).
813          * Note that 0 has special meaning and is therefore not adjusted.
814          */
815         domain.expires = ntohl (dnssl_opt->nd_opt_dnssl_lifetime);
816         if (domain.expires > 0)
817                 domain.expires += now + 10;
818
819         while (opt_len) {
820                 const char *domain_str;
821
822                 domain_str = parse_dnssl_domain (opt_ptr, opt_len);
823                 if (domain_str == NULL) {
824                         nm_log_dbg (LOGD_IP6, "(%s): invalid DNSSL option, parsing aborted",
825                                     device->iface);
826                         break;
827                 }
828
829                 /* The DNSSL encoding of domains happen to occupy the same size
830                  * as the length of the resulting string, including terminating
831                  * null. */
832                 opt_ptr += strlen (domain_str) + 1;
833                 opt_len -= strlen (domain_str) + 1;
834
835                 /* Ignore empty domains. They're probably just padding... */
836                 if (domain_str[0] == '\0')
837                         continue;
838
839                 /* Update cached domain information if we've seen this domain before */
840                 for (i = 0; i < device->dnssl_domains->len; i++) {
841                         cur_domain = &(g_array_index (device->dnssl_domains, NMIP6DNSSL, i));
842
843                         if (strcmp (domain_str, cur_domain->domain) != 0)
844                                 continue;
845
846                         cur_domain->expires = domain.expires;
847
848                         if (domain.expires > 0) {
849                                 nm_log_dbg (LOGD_IP6, "(%s): refreshing RA-provided domain %s (expires in %ld seconds)",
850                                             device->iface, domain_str,
851                                             domain.expires - now);
852                                 break;
853                         }
854
855                         nm_log_dbg (LOGD_IP6, "(%s): removing RA-provided domain %s on router request",
856                                     device->iface, domain_str);
857
858                         g_array_remove_index (device->dnssl_domains, i);
859                         changed = TRUE;
860                         break;
861                 }
862
863                 if (domain.expires == 0)
864                         continue;
865                 if (i < device->dnssl_domains->len)
866                         continue;
867
868                 nm_log_dbg (LOGD_IP6, "(%s): found RA-provided domain %s (expires in %ld seconds)",
869                             device->iface, domain_str, domain.expires - now);
870
871                 g_assert (strlen (domain_str) < sizeof (domain.domain));
872                 strcpy (domain.domain, domain_str);
873                 g_array_append_val (new_domains, domain);
874         }
875
876         /* New domains must be added in the order they are listed in the
877          * RA option and before any existing domains.
878          *
879          * Note: This is the place to remove domains if we want to cap the
880          *       number of domains. The RFC states that the one to expire
881          *       first of the existing domains should be removed.
882          */
883         if (new_domains->len) {
884                 g_array_prepend_vals (device->dnssl_domains,
885                                       new_domains->data, new_domains->len);
886                 changed = TRUE;
887         }
888
889         g_array_free (new_domains, TRUE);
890
891         /* Timeouts may have changed even if domains didn't */
892         set_dnssl_timeout (device);
893
894         return changed;
895 }
896
897 static NMIP6Device *
898 process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
899 {
900         NMIP6Device *device;
901         struct nduseroptmsg *ndmsg;
902         struct nd_opt_hdr *opt;
903         guint opts_len;
904         gboolean changed = FALSE;
905
906         nm_log_dbg (LOGD_IP6, "processing netlink nduseropt message");
907
908         ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg));
909
910         if (ndmsg->nduseropt_family != AF_INET6 ||
911                 ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
912                 ndmsg->nduseropt_icmp_code != 0) {
913                 nm_log_dbg (LOGD_IP6, "ignoring non-Router Advertisement message");
914                 return NULL;
915         }
916
917         device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex);
918         if (!device) {
919                 nm_log_dbg (LOGD_IP6, "ignoring message for unknown device");
920                 return NULL;
921         }
922
923         opt = (struct nd_opt_hdr *) (ndmsg + 1);
924         opts_len = ndmsg->nduseropt_opts_len;
925
926         while (opts_len >= sizeof (struct nd_opt_hdr)) {
927                 size_t nd_opt_len = opt->nd_opt_len;
928
929                 if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
930                         break;
931
932                 switch (opt->nd_opt_type) {
933                 case ND_OPT_RDNSS:
934                         changed = process_nduseropt_rdnss (device, opt);
935                         break;
936                 case ND_OPT_DNSSL:
937                         changed = process_nduseropt_dnssl (device, opt);
938                         break;
939                 }
940
941                 opts_len -= opt->nd_opt_len << 3;
942                 opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
943         }
944
945         if (changed)
946                 return device;
947         else
948                 return NULL;
949 }
950
951 static struct nla_policy link_policy[IFLA_MAX + 1] = {
952         [IFLA_PROTINFO] = { .type = NLA_NESTED },
953 };
954
955 static struct nla_policy link_prot_policy[IFLA_INET6_MAX + 1] = {
956         [IFLA_INET6_FLAGS]      = { .type = NLA_U32 },
957 };
958
959 static NMIP6Device *
960 process_newlink (NMIP6Manager *manager, struct nl_msg *msg)
961 {
962         struct nlmsghdr *hdr = nlmsg_hdr (msg);
963         struct ifinfomsg *ifi;
964         NMIP6Device *device;
965         struct nlattr *tb[IFLA_MAX + 1];
966         struct nlattr *pi[IFLA_INET6_MAX + 1];
967         int err;
968
969         ifi = nlmsg_data (hdr);
970         if (ifi->ifi_family != AF_INET6) {
971                 nm_log_dbg (LOGD_IP6, "ignoring netlink message family %d", ifi->ifi_family);
972                 return NULL;
973         }
974
975         device = nm_ip6_manager_get_device (manager, ifi->ifi_index);
976         if (!device || device->addrconf_complete) {
977                 nm_log_dbg (LOGD_IP6, "(%s): ignoring unknown or completed device",
978                             device ? device->iface : "(none)");
979                 return NULL;
980         }
981
982         /* FIXME: we have to do this manually for now since libnl doesn't yet
983          * support the IFLA_PROTINFO attribute of NEWLINK messages.  When it does,
984          * we can get rid of this function and just grab IFLA_PROTINFO from
985          * nm_ip6_device_sync_from_netlink(), then get the IFLA_INET6_FLAGS out of
986          * the PROTINFO.
987          */
988
989         err = nlmsg_parse (hdr, sizeof (*ifi), tb, IFLA_MAX, link_policy);
990         if (err < 0) {
991                 nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO attribute", device->iface);
992                 return NULL;
993         }
994         if (!tb[IFLA_PROTINFO]) {
995                 nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO attribute", device->iface);
996                 return NULL;
997         }
998
999         err = nla_parse_nested (pi, IFLA_INET6_MAX, tb[IFLA_PROTINFO], link_prot_policy);
1000         if (err < 0) {
1001                 nm_log_dbg (LOGD_IP6, "(%s): error parsing PROTINFO flags", device->iface);
1002                 return NULL;
1003         }
1004         if (!pi[IFLA_INET6_FLAGS]) {
1005                 nm_log_dbg (LOGD_IP6, "(%s): message had no PROTINFO flags", device->iface);
1006                 return NULL;
1007         }
1008
1009         device->ra_flags = nla_get_u32 (pi[IFLA_INET6_FLAGS]);
1010         nm_log_dbg (LOGD_IP6, "(%s): got IPv6 flags 0x%X", device->iface, device->ra_flags);
1011
1012         return device;
1013 }
1014
1015 static void
1016 netlink_notification (NMNetlinkMonitor *monitor, struct nl_msg *msg, gpointer user_data)
1017 {
1018         NMIP6Manager *manager = (NMIP6Manager *) user_data;
1019         NMIP6Device *device;
1020         struct nlmsghdr *hdr;
1021         gboolean config_changed = FALSE;
1022
1023         hdr = nlmsg_hdr (msg);
1024         nm_log_dbg (LOGD_HW, "netlink notificate type %d", hdr->nlmsg_type);
1025         switch (hdr->nlmsg_type) {
1026         case RTM_NEWADDR:
1027         case RTM_DELADDR:
1028                 device = process_addr (manager, msg);
1029                 config_changed = TRUE;
1030                 break;
1031         case RTM_NEWROUTE:
1032         case RTM_DELROUTE:
1033                 device = process_route (manager, msg);
1034                 config_changed = TRUE;
1035                 break;
1036         case RTM_NEWPREFIX:
1037                 device = process_prefix (manager, msg);
1038                 break;
1039         case RTM_NEWNDUSEROPT:
1040                 device = process_nduseropt (manager, msg);
1041                 config_changed = TRUE;
1042                 break;
1043         case RTM_NEWLINK:
1044                 device = process_newlink (manager, msg);
1045                 config_changed = TRUE;
1046                 break;
1047         default:
1048                 return;
1049         }
1050
1051         if (device) {
1052                 nm_log_dbg (LOGD_IP6, "(%s): syncing device with netlink changes", device->iface);
1053                 nm_ip6_device_sync_from_netlink (device, config_changed);
1054         }
1055 }
1056
1057 gboolean
1058 nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
1059                                   int ifindex,
1060                                   NMSettingIP6Config *s_ip6,
1061                                   const char *accept_ra_path)
1062 {
1063         NMIP6ManagerPrivate *priv;
1064         NMIP6Device *device;
1065         const char *method = NULL;
1066
1067         g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), FALSE);
1068         g_return_val_if_fail (ifindex > 0, FALSE);
1069
1070         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
1071
1072         device = nm_ip6_device_new (manager, ifindex);
1073         g_return_val_if_fail (device != NULL, FALSE);
1074         g_return_val_if_fail (   strchr (device->iface, '/') == NULL
1075                               && strcmp (device->iface, "all") != 0
1076                               && strcmp (device->iface, "default") != 0,
1077                               FALSE);
1078
1079         if (s_ip6)
1080                 method = nm_setting_ip6_config_get_method (s_ip6);
1081         if (!method)
1082                 method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
1083
1084         /* Establish target state and turn router advertisement acceptance on or off */
1085         if (!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
1086                 device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
1087                 nm_utils_do_sysctl (accept_ra_path, "0\n");
1088         } else {
1089                 device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
1090                 nm_utils_do_sysctl (accept_ra_path, "1\n");
1091         }
1092
1093         return TRUE;
1094 }
1095
1096 static gboolean
1097 poll_ip6_flags (gpointer user_data)
1098 {
1099         nm_netlink_monitor_request_ip6_info (NM_NETLINK_MONITOR (user_data), NULL);
1100         return TRUE;
1101 }
1102
1103 void
1104 nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex)
1105 {
1106         NMIP6ManagerPrivate *priv;
1107         NMIP6Device *device;
1108         CallbackInfo *info;
1109
1110         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
1111         g_return_if_fail (ifindex > 0);
1112
1113         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
1114
1115         device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
1116         g_return_if_fail (device != NULL);
1117
1118         nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface);
1119
1120         device->addrconf_complete = FALSE;
1121         device->ra_flags = 0;
1122
1123         /* Set up a timeout on the transaction to kill it after the timeout */
1124         info = callback_info_new (device, 0, FALSE);
1125         device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
1126                                                                  NM_IP6_TIMEOUT,
1127                                                                  finish_addrconf,
1128                                                                  info,
1129                                                                  (GDestroyNotify) g_free);
1130
1131         /* Bounce IPv6 on the interface to ensure the kernel will start looking for
1132          * new RAs; there doesn't seem to be a better way to do this right now.
1133          */
1134         if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) {
1135                 nm_utils_do_sysctl (device->disable_ip6_path, "1\n");
1136                 g_usleep (200);
1137                 nm_utils_do_sysctl (device->disable_ip6_path, "0\n");
1138         }
1139
1140         device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor);
1141
1142         /* Kick off the initial IPv6 flags request */
1143         nm_netlink_monitor_request_ip6_info (priv->monitor, NULL);
1144
1145         /* Sync flags, etc, from netlink; this will also notice if the
1146          * device is already fully configured and schedule the
1147          * ADDRCONF_COMPLETE signal in that case.
1148          */
1149         nm_ip6_device_sync_from_netlink (device, FALSE);
1150 }
1151
1152 void
1153 nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex)
1154 {
1155         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
1156         g_return_if_fail (ifindex > 0);
1157
1158         g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices,
1159                              GINT_TO_POINTER (ifindex));
1160 }
1161
1162 #define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m))
1163 #define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m))
1164
1165 #define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m))
1166 #define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m))
1167
1168 NMIP6Config *
1169 nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
1170 {
1171         NMIP6ManagerPrivate *priv;
1172         NMIP6Device *device;
1173         NMIP6Config *config;
1174         struct rtnl_addr *rtnladdr;
1175         struct nl_addr *nladdr;
1176         struct in6_addr *addr;
1177         NMIP6Address *ip6addr;
1178         struct rtnl_route *rtnlroute;
1179         struct nl_addr *nldest, *nlgateway;
1180         struct in6_addr *dest, *gateway;
1181         gboolean defgw_set = FALSE;
1182         struct in6_addr defgw;
1183         uint32_t metric;
1184         NMIP6Route *ip6route;
1185         int i;
1186
1187         g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
1188         g_return_val_if_fail (ifindex > 0, NULL);
1189
1190         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
1191
1192         device = (NMIP6Device *) g_hash_table_lookup (priv->devices,
1193                                                       GINT_TO_POINTER (ifindex));
1194         if (!device) {
1195                 nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex);
1196                 return NULL;
1197         }
1198
1199         config = nm_ip6_config_new ();
1200         if (!config) {
1201                 nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.",
1202                             device->iface);
1203                 return NULL;
1204         }
1205
1206         /* Make sure we refill the route and address caches, otherwise we won't get
1207          * up-to-date information here since the netlink route/addr change messages
1208          * may be lagging a bit.
1209          */
1210         nl_cache_refill (priv->nlh, priv->route_cache);
1211         nl_cache_refill (priv->nlh, priv->addr_cache);
1212
1213         /* Add routes */
1214         for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) {
1215                 /* Make sure it's an IPv6 route for this device */
1216                 if (rtnl_route_get_oif (rtnlroute) != device->ifindex)
1217                         continue;
1218                 if (rtnl_route_get_family (rtnlroute) != AF_INET6)
1219                         continue;
1220
1221                 nldest = rtnl_route_get_dst (rtnlroute);
1222                 if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
1223                         continue;
1224                 dest = nl_addr_get_binary_addr (nldest);
1225
1226                 nlgateway = rtnl_route_get_gateway (rtnlroute);
1227                 if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
1228                         continue;
1229                 gateway = nl_addr_get_binary_addr (nlgateway);
1230
1231                 if (rtnl_route_get_dst_len (rtnlroute) == 0) {
1232                         /* Default gateway route; don't add to normal routes but to each address */
1233                         if (!defgw_set) {
1234                                 memcpy (&defgw, gateway, sizeof (defgw));
1235                                 defgw_set = TRUE;
1236                         }
1237                         continue;
1238                 }
1239
1240                 /* Also ignore link-local routes where the destination and gateway are
1241                  * the same, which apparently get added by the kernel but return -EINVAL
1242                  * when we try to add them via netlink.
1243                  */
1244                 if (gateway && IN6_ARE_ADDR_EQUAL (dest, gateway))
1245                         continue;
1246
1247                 ip6route = nm_ip6_route_new ();
1248                 nm_ip6_route_set_dest (ip6route, dest);
1249                 nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
1250                 nm_ip6_route_set_next_hop (ip6route, gateway);
1251                 metric = rtnl_route_get_metric (rtnlroute, 1);
1252                 if (metric != UINT_MAX)
1253                         nm_ip6_route_set_metric (ip6route, metric);
1254                 nm_ip6_config_take_route (config, ip6route);
1255         }
1256
1257         /* Add addresses */
1258         for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) {
1259                 if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
1260                         continue;
1261
1262                 nladdr = rtnl_addr_get_local (rtnladdr);
1263                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
1264                         continue;
1265
1266                 addr = nl_addr_get_binary_addr (nladdr);
1267                 ip6addr = nm_ip6_address_new ();
1268                 nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
1269                 nm_ip6_address_set_address (ip6addr, addr);
1270                 nm_ip6_config_take_address (config, ip6addr);
1271                 if (defgw_set)
1272                         nm_ip6_address_set_gateway (ip6addr, &defgw);
1273         }
1274
1275         /* Add DNS servers */
1276         if (device->rdnss_servers) {
1277                 NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);
1278
1279                 for (i = 0; i < device->rdnss_servers->len; i++)
1280                         nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
1281         }
1282
1283         /* Add DNS domains */
1284         if (device->dnssl_domains) {
1285                 NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data);
1286
1287                 for (i = 0; i < device->dnssl_domains->len; i++)
1288                         nm_ip6_config_add_domain (config, dnssl[i].domain);
1289         }
1290
1291         return config;
1292 }
1293
1294 /******************************************************************/
1295
1296 static NMIP6Manager *
1297 nm_ip6_manager_new (void)
1298 {
1299         NMIP6Manager *manager;
1300         NMIP6ManagerPrivate *priv;
1301
1302         manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
1303         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
1304
1305         if (!priv->devices) {
1306                 nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables");
1307                 g_object_unref (manager);
1308                 manager = NULL;
1309         }
1310
1311         return manager;
1312 }
1313
1314 static NMIP6Manager *singleton = NULL;
1315
1316 NMIP6Manager *
1317 nm_ip6_manager_get (void)
1318 {
1319         if (!singleton) {
1320                 singleton = nm_ip6_manager_new ();
1321                 g_assert (singleton);
1322         } else
1323                 g_object_ref (singleton);
1324
1325         return singleton;
1326 }
1327
1328 static void
1329 nm_ip6_manager_init (NMIP6Manager *manager)
1330 {
1331         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
1332
1333         priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1334                                                NULL,
1335                                                (GDestroyNotify) nm_ip6_device_destroy);
1336
1337         priv->monitor = nm_netlink_monitor_get ();
1338         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL);
1339         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL);
1340         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL);
1341         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL);
1342
1343         priv->netlink_id = g_signal_connect (priv->monitor, "notification",
1344                                              G_CALLBACK (netlink_notification), manager);
1345
1346         priv->nlh = nm_netlink_get_default_handle ();
1347         priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh);
1348         priv->route_cache = rtnl_route_alloc_cache (priv->nlh);
1349 }
1350
1351 static void
1352 finalize (GObject *object)
1353 {
1354         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);
1355
1356         g_signal_handler_disconnect (priv->monitor, priv->netlink_id);
1357
1358         g_hash_table_destroy (priv->devices);
1359         g_object_unref (priv->monitor);
1360         nl_cache_free (priv->addr_cache);
1361         nl_cache_free (priv->route_cache);
1362
1363         singleton = NULL;
1364
1365         G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
1366 }
1367
1368 static void
1369 nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
1370 {
1371         GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
1372
1373         g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate));
1374
1375         /* virtual methods */
1376         object_class->finalize = finalize;
1377
1378         /* signals */
1379         signals[ADDRCONF_COMPLETE] =
1380                 g_signal_new ("addrconf-complete",
1381                                           G_OBJECT_CLASS_TYPE (object_class),
1382                                           G_SIGNAL_RUN_FIRST,
1383                                           G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
1384                                           NULL, NULL,
1385                                           _nm_marshal_VOID__INT_UINT_BOOLEAN,
1386                                           G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN);
1387
1388         signals[CONFIG_CHANGED] =
1389                 g_signal_new ("config-changed",
1390                                           G_OBJECT_CLASS_TYPE (object_class),
1391                                           G_SIGNAL_RUN_FIRST,
1392                                           G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
1393                                           NULL, NULL,
1394                                           _nm_marshal_VOID__INT_UINT_BOOLEAN,
1395                                           G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN);
1396 }
1397