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