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