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