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