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