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