ipv6: push router advertisement flags to listeners
[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/rtnl.h>
25 #include <netlink/route/route.h>
26
27 #include "nm-ip6-manager.h"
28 #include "nm-netlink-listener.h"
29 #include "NetworkManagerUtils.h"
30 #include "nm-marshal.h"
31 #include "nm-utils.h"
32
33 /* Pre-DHCP addrconf timeout, in seconds */
34 #define NM_IP6_TIMEOUT 10
35
36 /* FIXME? Stolen from the kernel sources */
37 #define IF_RA_OTHERCONF 0x80
38 #define IF_RA_MANAGED   0x40
39 #define IF_RA_RCVD      0x20
40 #define IF_RS_SENT      0x10
41
42 typedef struct {
43         NMNetlinkListener *netlink;
44         GHashTable *devices_by_iface, *devices_by_index;
45
46         struct nl_handle *nlh;
47         struct nl_cache *addr_cache, *route_cache;
48 } NMIP6ManagerPrivate;
49
50 #define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate))
51
52 typedef enum {
53         NM_IP6_DEVICE_UNCONFIGURED,
54         NM_IP6_DEVICE_GOT_LINK_LOCAL,
55         NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT,
56         NM_IP6_DEVICE_GOT_ADDRESS,
57         NM_IP6_DEVICE_WAITING_FOR_DHCP,
58         NM_IP6_DEVICE_GOT_DHCP,
59         NM_IP6_DEVICE_TIMED_OUT
60 } NMIP6DeviceState;
61
62 typedef struct {
63         struct in6_addr addr;
64         time_t expires;
65 } NMIP6RDNSS;
66
67 typedef struct {
68         NMIP6Manager *manager;
69         char *iface;
70         int index;
71
72         char *accept_ra_path;
73         gboolean accept_ra_save_valid;
74         guint32 accept_ra_save;
75
76         guint finish_addrconf_id;
77         guint config_changed_id;
78
79         NMIP6DeviceState state;
80         NMIP6DeviceState target_state;
81         gboolean addrconf_complete;
82
83         GArray *rdnss_servers;
84         guint rdnss_timeout_id;
85 } NMIP6Device;
86
87 G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT)
88
89 enum {
90         ADDRCONF_COMPLETE,
91         CONFIG_CHANGED,
92         LAST_SIGNAL
93 };
94
95 static guint signals[LAST_SIGNAL] = { 0 };
96
97 static NMIP6Manager *nm_ip6_manager_new (void);
98
99 static void netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data);
100
101 static void nm_ip6_device_destroy (NMIP6Device *device);
102
103 NMIP6Manager *
104 nm_ip6_manager_get (void)
105 {
106         static NMIP6Manager *singleton = NULL;
107
108         if (!singleton)
109                 singleton = nm_ip6_manager_new ();
110         g_assert (singleton);
111
112         return g_object_ref (singleton);
113 }
114
115 static void
116 nm_ip6_manager_init (NMIP6Manager *manager)
117 {
118         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
119
120         priv->devices_by_iface = g_hash_table_new_full (g_str_hash, g_str_equal,
121                                                                                                         NULL,
122                                                                                                         (GDestroyNotify) nm_ip6_device_destroy);
123         priv->devices_by_index = g_hash_table_new (NULL, NULL);
124
125         priv->netlink = nm_netlink_listener_get ();
126         g_signal_connect (priv->netlink, "notification",
127                                           G_CALLBACK (netlink_notification), manager);
128         nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_IFADDR, NULL);
129         nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_IPV6_PREFIX, NULL);
130         nm_netlink_listener_subscribe (priv->netlink, RTNLGRP_ND_USEROPT, NULL);
131
132         priv->nlh = nm_netlink_get_default_handle ();
133         priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh);
134         priv->route_cache = rtnl_route_alloc_cache (priv->nlh);
135 }
136
137 static void
138 finalize (GObject *object)
139 {
140         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);
141
142         g_hash_table_destroy (priv->devices_by_iface);
143         g_hash_table_destroy (priv->devices_by_index);
144         g_object_unref (priv->netlink);
145         nl_cache_free (priv->addr_cache);
146         nl_cache_free (priv->route_cache);
147
148         G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
149 }
150
151 static void
152 nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
153 {
154         GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
155
156         g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate));
157
158         /* virtual methods */
159         object_class->finalize = finalize;
160
161         /* signals */
162         signals[ADDRCONF_COMPLETE] =
163                 g_signal_new ("addrconf-complete",
164                                           G_OBJECT_CLASS_TYPE (object_class),
165                                           G_SIGNAL_RUN_FIRST,
166                                           G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
167                                           NULL, NULL,
168                                           _nm_marshal_VOID__STRING_UINT_BOOLEAN,
169                                           G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN);
170
171         signals[CONFIG_CHANGED] =
172                 g_signal_new ("config-changed",
173                                           G_OBJECT_CLASS_TYPE (object_class),
174                                           G_SIGNAL_RUN_FIRST,
175                                           G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
176                                           NULL, NULL,
177                                           _nm_marshal_VOID__STRING_UINT,
178                                           G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_UINT);
179 }
180
181 static void
182 nm_ip6_device_destroy (NMIP6Device *device)
183 {
184         g_return_if_fail (device != NULL);
185
186         /* reset the saved RA value */
187         if (device->accept_ra_save_valid) {
188                 nm_utils_do_sysctl (device->accept_ra_path,
189                                     device->accept_ra_save ? "1\n" : "0\n");
190         }
191
192         if (device->finish_addrconf_id)
193                 g_source_remove (device->finish_addrconf_id);
194         if (device->config_changed_id)
195                 g_source_remove (device->config_changed_id);
196         g_free (device->iface);
197         if (device->rdnss_servers)
198                 g_array_free (device->rdnss_servers, TRUE);
199         if (device->rdnss_timeout_id)
200                 g_source_remove (device->rdnss_timeout_id);
201
202         g_free (device->accept_ra_path);
203         g_slice_free (NMIP6Device, device);
204 }
205
206 static NMIP6Manager *
207 nm_ip6_manager_new (void)
208 {
209         NMIP6Manager *manager;
210         NMIP6ManagerPrivate *priv;
211
212         manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
213         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
214
215         if (!priv->devices_by_iface || !priv->devices_by_index) {
216                 nm_warning ("Error: not enough memory to initialize IP6 manager tables");
217                 g_object_unref (manager);
218                 manager = NULL;
219         }
220
221         return manager;
222 }
223
224 static NMIP6Device *
225 nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
226 {
227         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
228
229         return g_hash_table_lookup (priv->devices_by_index,
230                                                                 GINT_TO_POINTER (ifindex));
231 }
232
233 typedef struct {
234         NMIP6Device *device;
235         guint dhcp_opts;
236 } CallbackInfo;
237
238 static gboolean
239 finish_addrconf (gpointer user_data)
240 {
241         CallbackInfo *info = user_data;
242         NMIP6Device *device = info->device;
243         NMIP6Manager *manager = device->manager;
244         char *iface_copy;
245
246         device->finish_addrconf_id = 0;
247         device->addrconf_complete = TRUE;
248
249         if (device->state >= device->target_state) {
250                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
251                                            device->iface, info->dhcp_opts, TRUE);
252         } else {
253                 nm_info ("Device '%s' IP6 addrconf timed out or failed.",
254                                  device->iface);
255
256                 iface_copy = g_strdup (device->iface);
257
258                 nm_ip6_manager_cancel_addrconf (manager, device->iface);
259                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
260                                            iface_copy, info->dhcp_opts, FALSE);
261
262                 g_free (iface_copy);
263         }
264
265         return FALSE;
266 }
267
268 static gboolean
269 emit_config_changed (gpointer user_data)
270 {
271         CallbackInfo *info = user_data;
272         NMIP6Device *device = info->device;
273         NMIP6Manager *manager = device->manager;
274
275         device->config_changed_id = 0;
276         g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->iface, info->dhcp_opts);
277         return FALSE;
278 }
279
280 static void set_rdnss_timeout (NMIP6Device *device);
281
282 static gboolean
283 rdnss_expired (gpointer user_data)
284 {
285         NMIP6Device *device = user_data;
286         CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
287
288         set_rdnss_timeout (device);
289         emit_config_changed (&info);
290         return FALSE;
291 }
292
293 static void
294 set_rdnss_timeout (NMIP6Device *device)
295 {
296         time_t expires = 0, now = time (NULL);
297         NMIP6RDNSS *rdnss;
298         int i;
299
300         if (device->rdnss_timeout_id) {
301                 g_source_remove (device->rdnss_timeout_id);
302                 device->rdnss_timeout_id = 0;
303         }
304
305         /* Find the soonest expiration time. */
306         for (i = 0; i < device->rdnss_servers->len; i++) {
307                 rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i);
308                 if (rdnss->expires == 0)
309                         continue;
310
311                 /* If the entry has already expired, remove it; the "+ 1" is
312                  * because g_timeout_add_seconds() might fudge the timing a
313                  * bit.
314                  */
315                 if (rdnss->expires <= now + 1) {
316                         g_array_remove_index_fast (device->rdnss_servers, i--);
317                         continue;
318                 }
319
320                 if (!expires || rdnss->expires < expires)
321                         expires = rdnss->expires;
322         }
323
324         if (expires) {
325                 device->rdnss_timeout_id = g_timeout_add_seconds (expires - now,
326                                                                                                                   rdnss_expired,
327                                                                                                                   device);
328         }
329 }
330
331 static CallbackInfo *
332 callback_info_new (NMIP6Device *device, guint dhcp_opts)
333 {
334         CallbackInfo *info;
335
336         info = g_malloc0 (sizeof (CallbackInfo));
337         info->device = device;
338         info->dhcp_opts = dhcp_opts;
339
340         return info;
341 }
342
343 static void
344 nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
345 {
346         NMIP6Manager *manager = device->manager;
347         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
348         struct rtnl_addr *rtnladdr;
349         struct nl_addr *nladdr;
350         struct in6_addr *addr;
351         struct rtnl_link *link;
352         guint flags;
353         CallbackInfo *info;
354         guint dhcp_opts = IP6_DHCP_OPT_NONE;
355
356         for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache);
357                  rtnladdr;
358                  rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) {
359                 if (rtnl_addr_get_ifindex (rtnladdr) != device->index)
360                         continue;
361
362                 nladdr = rtnl_addr_get_local (rtnladdr);
363                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
364                         continue;
365
366                 addr = nl_addr_get_binary_addr (nladdr);
367                 if (IN6_IS_ADDR_LINKLOCAL (addr)) {
368                         if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
369                                 device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
370                 } else {
371                         if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
372                                 device->state = NM_IP6_DEVICE_GOT_ADDRESS;
373                 }
374         }
375
376         /* Note: we don't want to keep a cache of links, because the
377          * kernel doesn't send notifications when the flags change, so the
378          * cached rtnl_links would have out-of-date flags.
379          */
380         link = nm_netlink_index_to_rtnl_link (device->index);
381         flags = rtnl_link_get_flags (link);
382         rtnl_link_put (link);
383
384         if ((flags & IF_RA_RCVD) && device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT)
385                 device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
386
387         if (flags & IF_RA_MANAGED)
388                 dhcp_opts = IP6_DHCP_OPT_MANAGED;
389         else if (flags & IF_RA_OTHERCONF)
390                 dhcp_opts = IP6_DHCP_OPT_OTHERCONF;
391
392         if (!device->addrconf_complete) {
393                 if (device->state >= device->target_state ||
394                         device->state == NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT) {
395                         /* device->finish_addrconf_id may currently be a timeout
396                          * rather than an idle, so we remove the existing source.
397                          */
398                         if (device->finish_addrconf_id)
399                                 g_source_remove (device->finish_addrconf_id);
400
401                         info = callback_info_new (device, dhcp_opts);
402                         device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
403                                                                       finish_addrconf,
404                                                                       info,
405                                                                       (GDestroyNotify) g_free);
406                 }
407         } else if (config_changed) {
408                 if (!device->config_changed_id) {
409                         info = callback_info_new (device, dhcp_opts);
410                         device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
411                                                                      emit_config_changed,
412                                                                      info,
413                                                                      (GDestroyNotify) g_free);
414                 }
415         }
416 }
417
418 static void
419 ref_object (struct nl_object *obj, void *data)
420 {
421         struct nl_object **out = data;
422
423         nl_object_get (obj);
424         *out = obj;
425 }
426
427 static NMIP6Device *
428 process_addr (NMIP6Manager *manager, struct nl_msg *msg)
429 {
430         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
431         NMIP6Device *device;
432         struct rtnl_addr *rtnladdr;
433         int old_size;
434
435         rtnladdr = NULL;
436         nl_msg_parse (msg, ref_object, &rtnladdr);
437         if (!rtnladdr)
438                 return NULL;
439
440         device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));
441
442         old_size = nl_cache_nitems (priv->addr_cache);
443         nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL);
444         rtnl_addr_put (rtnladdr);
445
446         /* The kernel will re-notify us of automatically-added addresses
447          * every time it gets another router advertisement. We only want
448          * to notify higher levels if we actually changed something.
449          */
450         if (nl_cache_nitems (priv->addr_cache) == old_size)
451                 return NULL;
452
453         return device;
454 }
455
456 static NMIP6Device *
457 process_route (NMIP6Manager *manager, struct nl_msg *msg)
458 {
459         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
460         NMIP6Device *device;
461         struct rtnl_route *rtnlroute;
462         int old_size;
463
464         rtnlroute = NULL;
465         nl_msg_parse (msg, ref_object, &rtnlroute);
466         if (!rtnlroute)
467                 return NULL;
468
469         device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
470
471         old_size = nl_cache_nitems (priv->route_cache);
472         nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL);
473         rtnl_route_put (rtnlroute);
474
475         /* As above in process_addr */
476         if (nl_cache_nitems (priv->route_cache) == old_size)
477                 return NULL;
478
479         return device;
480 }
481
482 static NMIP6Device *
483 process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
484 {
485         struct prefixmsg *pmsg;
486         NMIP6Device *device;
487
488         /* We don't care about the prefix itself, but if we receive a
489          * router advertisement telling us to use DHCP, we might not
490          * get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only
491          * way to notice immediately that an RA was received.
492          */
493
494         pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg));
495         device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex);
496
497         if (!device || device->addrconf_complete)
498                 return NULL;
499
500         return device;
501 }
502
503 /* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard,
504  * RĂ©mi Denis-Courmont. GPLv2/3
505  */
506
507 #define ND_OPT_RDNSS 25
508 struct nd_opt_rdnss {
509         uint8_t nd_opt_rdnss_type;
510         uint8_t nd_opt_rdnss_len;
511         uint16_t nd_opt_rdnss_reserved1;
512         uint32_t nd_opt_rdnss_lifetime;
513         /* followed by one or more IPv6 addresses */
514 };
515
516 static NMIP6Device *
517 process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
518 {
519         NMIP6Device *device;
520         struct nduseroptmsg *ndmsg;
521         struct nd_opt_hdr *opt;
522         guint opts_len, i;
523         time_t now = time (NULL);
524         struct nd_opt_rdnss *rdnss_opt;
525         struct in6_addr *addr;
526         GArray *servers;
527         NMIP6RDNSS server, *sa, *sb;
528         gboolean changed;
529
530         ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg));
531
532         if (ndmsg->nduseropt_family != AF_INET6 ||
533                 ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
534                 ndmsg->nduseropt_icmp_code != 0)
535                 return NULL;
536
537         device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex);
538         if (!device)
539                 return NULL;
540
541         servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
542
543         opt = (struct nd_opt_hdr *) (ndmsg + 1);
544         opts_len = ndmsg->nduseropt_opts_len;
545
546         while (opts_len >= sizeof (struct nd_opt_hdr)) {
547                 size_t nd_opt_len = opt->nd_opt_len;
548
549                 if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
550                         break;
551
552                 if (opt->nd_opt_type != ND_OPT_RDNSS)
553                         goto next;
554
555                 if (nd_opt_len < 3 || (nd_opt_len & 1) == 0)
556                         goto next;
557
558                 rdnss_opt = (struct nd_opt_rdnss *) opt;
559
560                 server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime);
561                 for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) {
562                         server.addr = *addr;
563                         g_array_append_val (servers, server);
564                 }
565
566         next:
567                 opts_len -= opt->nd_opt_len << 3;
568                 opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
569         }
570
571         /* See if anything (other than expiration time) changed */
572         if (servers->len != device->rdnss_servers->len)
573                 changed = TRUE;
574         else {
575                 for (i = 0; i < servers->len; i++) {
576                         sa = &(g_array_index (servers, NMIP6RDNSS, i));
577                         sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
578                         if (memcmp (&sa->addr, &sb->addr, sizeof (struct in6_addr)) != 0) {
579                                 changed = TRUE;
580                                 break;
581                         }
582                 }
583                 changed = FALSE;
584         }
585
586         if (changed) {
587                 g_array_free (device->rdnss_servers, TRUE);
588                 device->rdnss_servers = servers;
589         } else
590                 g_array_free (servers, TRUE);
591
592         /* Timeouts may have changed even if IPs didn't */
593         set_rdnss_timeout (device);
594
595         if (changed)
596                 return device;
597         else
598                 return NULL;
599 }
600
601 static void
602 netlink_notification (NMNetlinkListener *listener, struct nl_msg *msg, gpointer user_data)
603 {
604         NMIP6Manager *manager = (NMIP6Manager *) user_data;
605         NMIP6Device *device;
606         struct nlmsghdr *hdr;
607         gboolean config_changed = FALSE;
608
609         hdr = nlmsg_hdr (msg);
610         switch (hdr->nlmsg_type) {
611         case RTM_NEWADDR:
612         case RTM_DELADDR:
613                 device = process_addr (manager, msg);
614                 config_changed = TRUE;
615                 break;
616
617         case RTM_NEWROUTE:
618         case RTM_DELROUTE:
619                 device = process_route (manager, msg);
620                 config_changed = TRUE;
621                 break;
622
623         case RTM_NEWPREFIX:
624                 device = process_prefix (manager, msg);
625                 break;
626
627         case RTM_NEWNDUSEROPT:
628                 device = process_nduseropt (manager, msg);
629                 config_changed = TRUE;
630                 break;
631
632         default:
633                 return;
634         }
635
636         if (device)
637                 nm_ip6_device_sync_from_netlink (device, config_changed);
638 }
639
640 static NMIP6Device *
641 nm_ip6_device_new (NMIP6Manager *manager, const char *iface)
642 {
643         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
644         NMIP6Device *device;
645         GError *error = NULL;
646         char *contents = NULL;
647
648         device = g_slice_new0 (NMIP6Device);
649         if (!device) {
650                 nm_warning ("%s: Out of memory creating IP6 addrconf object.", iface);
651                 return NULL;
652         }
653
654         device->iface = g_strdup (iface);
655         if (!device->iface) {
656                 nm_warning ("%s: Out of memory creating IP6 addrconf object "
657                             "property 'iface'.",
658                             iface);
659                 goto error;
660         }
661         device->index = nm_netlink_iface_to_index (iface);
662
663         device->accept_ra_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra", iface);
664         if (!device->accept_ra_path) {
665                 nm_warning ("%s: Out of memory creating IP6 addrconf object "
666                             "property 'accept_ra_path'.",
667                             iface);
668                 goto error;
669         }
670
671         device->manager = manager;
672
673         device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
674
675         g_hash_table_replace (priv->devices_by_iface, device->iface, device);
676         g_hash_table_replace (priv->devices_by_index, GINT_TO_POINTER (device->index), device);
677
678         /* Grab the original value of "accept_ra" so we can restore it when the
679          * device is taken down.
680          */
681         if (!g_file_get_contents (device->accept_ra_path, &contents, NULL, &error)) {
682                 nm_warning ("%s: error reading %s: (%d) %s",
683                             iface, device->accept_ra_path,
684                             error ? error->code : -1,
685                             error && error->message ? error->message : "(unknown)");
686                 g_clear_error (&error);
687         } else {
688                 long int tmp;
689
690                 errno = 0;
691                 tmp = strtol (contents, NULL, 10);
692                 if ((errno == 0) && (tmp == 0 || tmp == 1)) {
693                         device->accept_ra_save = (guint32) tmp;
694                         device->accept_ra_save_valid = TRUE;
695                 }
696                 g_free (contents);
697         }
698
699         return device;
700
701 error:
702         nm_ip6_device_destroy (device);
703         return NULL;
704 }
705
706 void
707 nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
708                                                                   const char *iface,
709                                                                   NMSettingIP6Config *s_ip6)
710 {
711         NMIP6ManagerPrivate *priv;
712         NMIP6Device *device;
713         const char *method = NULL;
714
715         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
716         g_return_if_fail (iface != NULL);
717
718         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
719
720         device = nm_ip6_device_new (manager, iface);
721
722         if (s_ip6)
723                 method = nm_setting_ip6_config_get_method (s_ip6);
724         if (!method)
725                 method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
726
727         if (   !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)
728                 || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL))
729                 device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
730         else
731                 device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
732
733         g_return_if_fail (strchr (iface, '/') == NULL &&
734                                           strcmp (iface, "all") != 0 &&
735                                           strcmp (iface, "default") != 0);
736
737         /* Turn router advertisement acceptance on or off... */
738         nm_utils_do_sysctl (device->accept_ra_path,
739                             device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS ? "1\n" : "0\n");
740 }
741
742 void
743 nm_ip6_manager_begin_addrconf (NMIP6Manager *manager,
744                                                            const char *iface)
745 {
746         NMIP6ManagerPrivate *priv;
747         NMIP6Device *device;
748         CallbackInfo *info;
749
750         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
751         g_return_if_fail (iface != NULL);
752
753         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
754
755         device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface);
756         g_return_if_fail (device != NULL);
757
758         nm_info ("Activation (%s) Beginning IP6 addrconf.", iface);
759
760         device->addrconf_complete = FALSE;
761
762         /* Set up a timeout on the transaction to kill it after the timeout */
763         info = callback_info_new (device, 0);
764         device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
765                                                                  NM_IP6_TIMEOUT,
766                                                                  finish_addrconf,
767                                                                  info,
768                                                                  (GDestroyNotify) g_free);
769
770         /* Sync flags, etc, from netlink; this will also notice if the
771          * device is already fully configured and schedule the
772          * ADDRCONF_COMPLETE signal in that case.
773          */
774         nm_ip6_device_sync_from_netlink (device, FALSE);
775 }
776
777 void
778 nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager,
779                                                                 const char *iface)
780 {
781         NMIP6ManagerPrivate *priv;
782         NMIP6Device *device;
783
784         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
785         g_return_if_fail (iface != NULL);
786
787         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
788
789         device = g_hash_table_lookup (priv->devices_by_iface, iface);
790         if (device) {
791                 g_hash_table_remove (priv->devices_by_index, GINT_TO_POINTER (device->index));
792                 g_hash_table_remove (priv->devices_by_iface, iface);
793         }
794 }
795
796 NMIP6Config *
797 nm_ip6_manager_get_ip6_config (NMIP6Manager *manager,
798                                                            const char *iface)
799 {
800         NMIP6ManagerPrivate *priv;
801         NMIP6Device *device;
802         NMIP6Config *config;
803         struct rtnl_addr *rtnladdr;
804         struct nl_addr *nladdr;
805         struct in6_addr *addr;
806         NMIP6Address *ip6addr;
807         struct rtnl_route *rtnlroute;
808         struct nl_addr *nldest, *nlgateway;
809         struct in6_addr *dest, *gateway;
810         uint32_t metric;
811         NMIP6Route *ip6route;
812         int i;
813
814         g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
815         g_return_val_if_fail (iface != NULL, NULL);
816
817         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
818
819         device = (NMIP6Device *) g_hash_table_lookup (priv->devices_by_iface, iface);
820         if (!device) {
821                 nm_warning ("Device '%s' addrconf not started.", iface);
822                 return NULL;
823         }
824
825         config = nm_ip6_config_new ();
826         if (!config) {
827                 nm_warning ("%s: Out of memory creating IP6 config object.",
828                             iface);
829                 return NULL;
830         }
831
832         /* Add addresses */
833         for (rtnladdr = (struct rtnl_addr *)nl_cache_get_first (priv->addr_cache);
834                  rtnladdr;
835                  rtnladdr = (struct rtnl_addr *)nl_cache_get_next ((struct nl_object *)rtnladdr)) {
836                 if (rtnl_addr_get_ifindex (rtnladdr) != device->index)
837                         continue;
838
839                 nladdr = rtnl_addr_get_local (rtnladdr);
840                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
841                         continue;
842
843                 addr = nl_addr_get_binary_addr (nladdr);
844                 ip6addr = nm_ip6_address_new ();
845                 nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
846                 nm_ip6_address_set_address (ip6addr, addr);
847                 nm_ip6_config_take_address (config, ip6addr);
848         }
849
850         /* Add routes */
851         for (rtnlroute = (struct rtnl_route *)nl_cache_get_first (priv->route_cache);
852                  rtnlroute;
853                  rtnlroute = (struct rtnl_route *)nl_cache_get_next ((struct nl_object *)rtnlroute)) {
854                 if (rtnl_route_get_oif (rtnlroute) != device->index)
855                         continue;
856
857                 nldest = rtnl_route_get_dst (rtnlroute);
858                 if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
859                         continue;
860                 dest = nl_addr_get_binary_addr (nldest);
861
862                 nlgateway = rtnl_route_get_gateway (rtnlroute);
863                 if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
864                         continue;
865                 gateway = nl_addr_get_binary_addr (nlgateway);
866
867                 ip6route = nm_ip6_route_new ();
868                 nm_ip6_route_set_dest (ip6route, dest);
869                 nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
870                 nm_ip6_route_set_next_hop (ip6route, gateway);
871                 metric = rtnl_route_get_metric (rtnlroute, 1);
872                 if (metric != UINT_MAX)
873                         nm_ip6_route_set_metric (ip6route, metric);
874                 nm_ip6_config_take_route (config, ip6route);
875         }
876
877         /* Add DNS servers */
878         if (device->rdnss_servers) {
879                 NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);
880
881                 for (i = 0; i < device->rdnss_servers->len; i++)
882                         nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
883         }
884
885         return config;
886 }