ip6: fix a crash when /proc/sys/net/ipv6/conf/<iface>/* files are not present
[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 "NetworkManagerUtils.h"
31 #include "nm-marshal.h"
32 #include "nm-logging.h"
33 #include "nm-system.h"
34
35 /* Pre-DHCP addrconf timeout, in seconds */
36 #define NM_IP6_TIMEOUT 10
37
38 /* FIXME? Stolen from the kernel sources */
39 #define IF_RA_OTHERCONF 0x80
40 #define IF_RA_MANAGED   0x40
41 #define IF_RA_RCVD      0x20
42 #define IF_RS_SENT      0x10
43
44 typedef struct {
45         NMNetlinkMonitor *monitor;
46         GHashTable *devices;
47
48         struct nl_handle *nlh;
49         struct nl_cache *addr_cache, *route_cache;
50 } NMIP6ManagerPrivate;
51
52 #define NM_IP6_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_IP6_MANAGER, NMIP6ManagerPrivate))
53
54 G_DEFINE_TYPE (NMIP6Manager, nm_ip6_manager, G_TYPE_OBJECT)
55
56 enum {
57         ADDRCONF_COMPLETE,
58         CONFIG_CHANGED,
59         LAST_SIGNAL
60 };
61
62 static guint signals[LAST_SIGNAL] = { 0 };
63
64 typedef enum {
65         NM_IP6_DEVICE_UNCONFIGURED,
66         NM_IP6_DEVICE_GOT_LINK_LOCAL,
67         NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT,
68         NM_IP6_DEVICE_GOT_ADDRESS,
69         NM_IP6_DEVICE_TIMED_OUT
70 } NMIP6DeviceState;
71
72 typedef struct {
73         struct in6_addr addr;
74         time_t expires;
75 } NMIP6RDNSS;
76
77 /******************************************************************/
78
79 typedef struct {
80         NMIP6Manager *manager;
81         char *iface;
82         int ifindex;
83
84         char *accept_ra_path;
85         gboolean accept_ra_save_valid;
86         guint32 accept_ra_save;
87
88         char *disable_ip6_path;
89         gboolean disable_ip6_save_valid;
90         guint32 disable_ip6_save;
91
92         guint finish_addrconf_id;
93         guint config_changed_id;
94
95         NMIP6DeviceState state;
96         NMIP6DeviceState target_state;
97         gboolean addrconf_complete;
98
99         GArray *rdnss_servers;
100         guint rdnss_timeout_id;
101
102         guint ip6flags_poll_id;
103
104         guint32 ra_flags;
105 } NMIP6Device;
106
107 static void
108 nm_ip6_device_destroy (NMIP6Device *device)
109 {
110         g_return_if_fail (device != NULL);
111
112         /* reset the saved RA value */
113         if (device->accept_ra_save_valid) {
114                 nm_utils_do_sysctl (device->accept_ra_path,
115                                     device->accept_ra_save ? "1\n" : "0\n");
116         }
117
118         /* reset the saved IPv6 value */
119         if (device->disable_ip6_save_valid) {
120                 nm_utils_do_sysctl (device->disable_ip6_path,
121                                     device->disable_ip6_save ? "1\n" : "0\n");
122         }
123
124         if (device->finish_addrconf_id)
125                 g_source_remove (device->finish_addrconf_id);
126         if (device->config_changed_id)
127                 g_source_remove (device->config_changed_id);
128         g_free (device->iface);
129         if (device->rdnss_servers)
130                 g_array_free (device->rdnss_servers, TRUE);
131         if (device->rdnss_timeout_id)
132                 g_source_remove (device->rdnss_timeout_id);
133         if (device->ip6flags_poll_id)
134                 g_source_remove (device->ip6flags_poll_id);
135
136         g_free (device->accept_ra_path);
137         g_slice_free (NMIP6Device, device);
138 }
139
140 static gboolean
141 get_proc_sys_net_value (const char *path, const char *iface, guint32 *out_value)
142 {
143         GError *error = NULL;
144         char *contents = NULL;
145         gboolean success = FALSE;
146         long int tmp;
147
148         if (!g_file_get_contents (path, &contents, NULL, &error)) {
149                 nm_log_warn (LOGD_IP6, "(%s): error reading %s: (%d) %s",
150                              iface, path,
151                              error ? error->code : -1,
152                              error && error->message ? error->message : "(unknown)");
153                 g_clear_error (&error);
154         } else {
155                 errno = 0;
156                 tmp = strtol (contents, NULL, 10);
157                 if ((errno == 0) && (tmp == 0 || tmp == 1)) {
158                         *out_value = (guint32) tmp;
159                         success = TRUE;
160                 }
161                 g_free (contents);
162         }
163
164         return success;
165 }
166
167 static NMIP6Device *
168 nm_ip6_device_new (NMIP6Manager *manager, int ifindex)
169 {
170         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
171         NMIP6Device *device;
172
173         g_return_val_if_fail (ifindex > 0, NULL);
174
175         device = g_slice_new0 (NMIP6Device);
176         if (!device) {
177                 nm_log_err (LOGD_IP6, "(%d): out of memory creating IP6 addrconf object.",
178                             ifindex);
179                 return NULL;
180         }
181
182         device->ifindex = ifindex;
183         device->iface = g_strdup (nm_netlink_index_to_iface (ifindex));
184         if (!device->iface) {
185                 nm_log_err (LOGD_IP6, "(%d): could not find interface name from index.",
186                             ifindex);
187                 goto error;
188         }
189
190         device->manager = manager;
191
192         device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
193
194         g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device);
195
196         /* Grab the original value of "accept_ra" so we can restore it when the
197          * device is taken down.
198          */
199         device->accept_ra_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/accept_ra",
200                                                   device->iface);
201         g_assert (device->accept_ra_path);
202         device->accept_ra_save_valid = get_proc_sys_net_value (device->accept_ra_path,
203                                                                device->iface,
204                                                                &device->accept_ra_save);
205
206         /* and the original value of IPv6 enable/disable */
207         device->disable_ip6_path = g_strdup_printf ("/proc/sys/net/ipv6/conf/%s/disable_ipv6",
208                                                     device->iface);
209         g_assert (device->disable_ip6_path);
210         device->disable_ip6_save_valid = get_proc_sys_net_value (device->disable_ip6_path,
211                                                                  device->iface,
212                                                                  &device->disable_ip6_save);
213
214         return device;
215
216 error:
217         nm_ip6_device_destroy (device);
218         return NULL;
219 }
220
221 static NMIP6Device *
222 nm_ip6_manager_get_device (NMIP6Manager *manager, int ifindex)
223 {
224         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
225
226         return g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
227 }
228
229 /******************************************************************/
230
231 typedef struct {
232         NMIP6Device *device;
233         guint dhcp_opts;
234         gboolean success;
235 } CallbackInfo;
236
237 static gboolean
238 finish_addrconf (gpointer user_data)
239 {
240         CallbackInfo *info = user_data;
241         NMIP6Device *device = info->device;
242         NMIP6Manager *manager = device->manager;
243         int ifindex;
244
245         device->finish_addrconf_id = 0;
246         device->addrconf_complete = TRUE;
247         ifindex = device->ifindex;
248
249         /* We're done, stop polling IPv6 flags */
250         if (device->ip6flags_poll_id) {
251                 g_source_remove (device->ip6flags_poll_id);
252                 device->ip6flags_poll_id = 0;
253         }
254
255         /* And tell listeners that addrconf is complete */
256         if (info->success) {
257                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
258                                ifindex, info->dhcp_opts, TRUE);
259         } else {
260                 nm_log_info (LOGD_IP6, "(%s): IP6 addrconf timed out or failed.",
261                              device->iface);
262
263                 nm_ip6_manager_cancel_addrconf (manager, ifindex);
264                 g_signal_emit (manager, signals[ADDRCONF_COMPLETE], 0,
265                                ifindex, info->dhcp_opts, FALSE);
266         }
267
268         return FALSE;
269 }
270
271 static gboolean
272 emit_config_changed (gpointer user_data)
273 {
274         CallbackInfo *info = user_data;
275         NMIP6Device *device = info->device;
276         NMIP6Manager *manager = device->manager;
277
278         device->config_changed_id = 0;
279         g_signal_emit (manager, signals[CONFIG_CHANGED], 0, device->ifindex, info->dhcp_opts);
280         return FALSE;
281 }
282
283 static void set_rdnss_timeout (NMIP6Device *device);
284
285 static gboolean
286 rdnss_expired (gpointer user_data)
287 {
288         NMIP6Device *device = user_data;
289         CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
290
291         set_rdnss_timeout (device);
292         emit_config_changed (&info);
293         return FALSE;
294 }
295
296 static void
297 set_rdnss_timeout (NMIP6Device *device)
298 {
299         time_t expires = 0, now = time (NULL);
300         NMIP6RDNSS *rdnss;
301         int i;
302
303         if (device->rdnss_timeout_id) {
304                 g_source_remove (device->rdnss_timeout_id);
305                 device->rdnss_timeout_id = 0;
306         }
307
308         /* Find the soonest expiration time. */
309         for (i = 0; i < device->rdnss_servers->len; i++) {
310                 rdnss = &g_array_index (device->rdnss_servers, NMIP6RDNSS, i);
311                 if (rdnss->expires == 0)
312                         continue;
313
314                 /* If the entry has already expired, remove it; the "+ 1" is
315                  * because g_timeout_add_seconds() might fudge the timing a
316                  * bit.
317                  */
318                 if (rdnss->expires <= now + 1) {
319                         g_array_remove_index_fast (device->rdnss_servers, i--);
320                         continue;
321                 }
322
323                 if (!expires || rdnss->expires < expires)
324                         expires = rdnss->expires;
325         }
326
327         if (expires) {
328                 device->rdnss_timeout_id = g_timeout_add_seconds (expires - now,
329                                                                                                                   rdnss_expired,
330                                                                                                                   device);
331         }
332 }
333
334 static CallbackInfo *
335 callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success)
336 {
337         CallbackInfo *info;
338
339         info = g_malloc0 (sizeof (CallbackInfo));
340         info->device = device;
341         info->dhcp_opts = dhcp_opts;
342         info->success = success;
343         return info;
344 }
345
346 static void
347 nm_ip6_device_sync_from_netlink (NMIP6Device *device, gboolean config_changed)
348 {
349         NMIP6Manager *manager = device->manager;
350         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
351         struct rtnl_addr *rtnladdr;
352         struct nl_addr *nladdr;
353         struct in6_addr *addr;
354         CallbackInfo *info;
355         guint dhcp_opts = IP6_DHCP_OPT_NONE;
356
357         /* Look for any IPv6 addresses the kernel may have set for the device */
358         for (rtnladdr = (struct rtnl_addr *) nl_cache_get_first (priv->addr_cache);
359                  rtnladdr;
360                  rtnladdr = (struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) rtnladdr)) {
361                 if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
362                         continue;
363
364                 nladdr = rtnl_addr_get_local (rtnladdr);
365                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
366                         continue;
367
368                 addr = nl_addr_get_binary_addr (nladdr);
369                 if (IN6_IS_ADDR_LINKLOCAL (addr)) {
370                         if (device->state == NM_IP6_DEVICE_UNCONFIGURED)
371                                 device->state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
372                 } else {
373                         if (device->state < NM_IP6_DEVICE_GOT_ADDRESS)
374                                 device->state = NM_IP6_DEVICE_GOT_ADDRESS;
375                 }
376         }
377
378         /* We only care about router advertisements if we want for a real IPv6 address */
379         if (device->target_state == NM_IP6_DEVICE_GOT_ADDRESS) {
380                 if (   (device->ra_flags & IF_RA_RCVD)
381                     && (device->state < NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT))
382                         device->state = NM_IP6_DEVICE_GOT_ROUTER_ADVERTISEMENT;
383
384                 if (device->ra_flags & IF_RA_MANAGED) {
385                         dhcp_opts = IP6_DHCP_OPT_MANAGED;
386                         nm_log_dbg (LOGD_IP6, "router advertisement deferred to DHCPv6");
387                 } else if (device->ra_flags & IF_RA_OTHERCONF) {
388                         dhcp_opts = IP6_DHCP_OPT_OTHERCONF;
389                         nm_log_dbg (LOGD_IP6, "router advertisement requests parallel DHCPv6");
390                 }
391         }
392
393         if (!device->addrconf_complete) {
394                 /* Managed mode (ie DHCP only) short-circuits automatic addrconf, so
395                  * we don't bother waiting for the device's target state to be reached
396                  * when the RA requests managed mode.
397                  */
398                 if (   (device->state >= device->target_state)
399                     || (dhcp_opts == IP6_DHCP_OPT_MANAGED)) {
400                         /* device->finish_addrconf_id may currently be a timeout
401                          * rather than an idle, so we remove the existing source.
402                          */
403                         if (device->finish_addrconf_id)
404                                 g_source_remove (device->finish_addrconf_id);
405
406                         info = callback_info_new (device, dhcp_opts, TRUE);
407                         device->finish_addrconf_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
408                                                                       finish_addrconf,
409                                                                       info,
410                                                                       (GDestroyNotify) g_free);
411                 }
412         } else if (config_changed) {
413                 if (!device->config_changed_id) {
414                         info = callback_info_new (device, dhcp_opts, TRUE);
415                         device->config_changed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
416                                                                      emit_config_changed,
417                                                                      info,
418                                                                      (GDestroyNotify) g_free);
419                 }
420         }
421 }
422
423 static void
424 ref_object (struct nl_object *obj, void *data)
425 {
426         struct nl_object **out = data;
427
428         nl_object_get (obj);
429         *out = obj;
430 }
431
432 static NMIP6Device *
433 process_addr (NMIP6Manager *manager, struct nl_msg *msg)
434 {
435         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
436         NMIP6Device *device;
437         struct rtnl_addr *rtnladdr;
438         int old_size;
439
440         rtnladdr = NULL;
441         nl_msg_parse (msg, ref_object, &rtnladdr);
442         if (!rtnladdr)
443                 return NULL;
444
445         device = nm_ip6_manager_get_device (manager, rtnl_addr_get_ifindex (rtnladdr));
446
447         old_size = nl_cache_nitems (priv->addr_cache);
448         nl_cache_include (priv->addr_cache, (struct nl_object *)rtnladdr, NULL);
449         rtnl_addr_put (rtnladdr);
450
451         /* The kernel will re-notify us of automatically-added addresses
452          * every time it gets another router advertisement. We only want
453          * to notify higher levels if we actually changed something.
454          */
455         if (nl_cache_nitems (priv->addr_cache) == old_size)
456                 return NULL;
457
458         return device;
459 }
460
461 static NMIP6Device *
462 process_route (NMIP6Manager *manager, struct nl_msg *msg)
463 {
464         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
465         NMIP6Device *device;
466         struct rtnl_route *rtnlroute;
467         int old_size;
468
469         rtnlroute = NULL;
470         nl_msg_parse (msg, ref_object, &rtnlroute);
471         if (!rtnlroute)
472                 return NULL;
473
474         device = nm_ip6_manager_get_device (manager, rtnl_route_get_oif (rtnlroute));
475
476         old_size = nl_cache_nitems (priv->route_cache);
477         nl_cache_include (priv->route_cache, (struct nl_object *)rtnlroute, NULL);
478         rtnl_route_put (rtnlroute);
479
480         /* As above in process_addr */
481         if (nl_cache_nitems (priv->route_cache) == old_size)
482                 return NULL;
483
484         return device;
485 }
486
487 static NMIP6Device *
488 process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
489 {
490         struct prefixmsg *pmsg;
491         NMIP6Device *device;
492
493         /* We don't care about the prefix itself, but if we receive a
494          * router advertisement telling us to use DHCP, we might not
495          * get any RTM_NEWADDRs or RTM_NEWROUTEs, so this is our only
496          * way to notice immediately that an RA was received.
497          */
498
499         pmsg = (struct prefixmsg *) NLMSG_DATA (nlmsg_hdr (msg));
500         device = nm_ip6_manager_get_device (manager, pmsg->prefix_ifindex);
501
502         if (!device || device->addrconf_complete)
503                 return NULL;
504
505         return device;
506 }
507
508 /* RDNSS parsing code based on rdnssd, Copyright 2007 Pierre Ynard,
509  * RĂ©mi Denis-Courmont. GPLv2/3
510  */
511
512 #define ND_OPT_RDNSS 25
513 struct nd_opt_rdnss {
514         uint8_t nd_opt_rdnss_type;
515         uint8_t nd_opt_rdnss_len;
516         uint16_t nd_opt_rdnss_reserved1;
517         uint32_t nd_opt_rdnss_lifetime;
518         /* followed by one or more IPv6 addresses */
519 };
520
521 static NMIP6Device *
522 process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
523 {
524         NMIP6Device *device;
525         struct nduseroptmsg *ndmsg;
526         struct nd_opt_hdr *opt;
527         guint opts_len, i;
528         time_t now = time (NULL);
529         struct nd_opt_rdnss *rdnss_opt;
530         struct in6_addr *addr;
531         GArray *servers;
532         NMIP6RDNSS server, *sa, *sb;
533         gboolean changed;
534
535         ndmsg = (struct nduseroptmsg *) NLMSG_DATA (nlmsg_hdr (msg));
536
537         if (ndmsg->nduseropt_family != AF_INET6 ||
538                 ndmsg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||
539                 ndmsg->nduseropt_icmp_code != 0)
540                 return NULL;
541
542         device = nm_ip6_manager_get_device (manager, ndmsg->nduseropt_ifindex);
543         if (!device)
544                 return NULL;
545
546         servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
547
548         opt = (struct nd_opt_hdr *) (ndmsg + 1);
549         opts_len = ndmsg->nduseropt_opts_len;
550
551         while (opts_len >= sizeof (struct nd_opt_hdr)) {
552                 size_t nd_opt_len = opt->nd_opt_len;
553
554                 if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
555                         break;
556
557                 if (opt->nd_opt_type != ND_OPT_RDNSS)
558                         goto next;
559
560                 if (nd_opt_len < 3 || (nd_opt_len & 1) == 0)
561                         goto next;
562
563                 rdnss_opt = (struct nd_opt_rdnss *) opt;
564
565                 server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime);
566                 for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) {
567                         server.addr = *addr;
568                         g_array_append_val (servers, server);
569                 }
570
571         next:
572                 opts_len -= opt->nd_opt_len << 3;
573                 opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
574         }
575
576         /* See if anything (other than expiration time) changed */
577         if (servers->len != device->rdnss_servers->len)
578                 changed = TRUE;
579         else {
580                 for (i = 0; i < servers->len; i++) {
581                         sa = &(g_array_index (servers, NMIP6RDNSS, i));
582                         sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
583                         if (memcmp (&sa->addr, &sb->addr, sizeof (struct in6_addr)) != 0) {
584                                 changed = TRUE;
585                                 break;
586                         }
587                 }
588                 changed = FALSE;
589         }
590
591         if (changed) {
592                 g_array_free (device->rdnss_servers, TRUE);
593                 device->rdnss_servers = servers;
594         } else
595                 g_array_free (servers, TRUE);
596
597         /* Timeouts may have changed even if IPs didn't */
598         set_rdnss_timeout (device);
599
600         if (changed)
601                 return device;
602         else
603                 return NULL;
604 }
605
606 static struct nla_policy link_policy[IFLA_MAX + 1] = {
607         [IFLA_PROTINFO] = { .type = NLA_NESTED },
608 };
609
610 static struct nla_policy link_prot_policy[IFLA_INET6_MAX + 1] = {
611         [IFLA_INET6_FLAGS]      = { .type = NLA_U32 },
612 };
613
614 static NMIP6Device *
615 process_newlink (NMIP6Manager *manager, struct nl_msg *msg)
616 {
617         struct nlmsghdr *hdr = nlmsg_hdr (msg);
618         struct ifinfomsg *ifi;
619         NMIP6Device *device;
620         struct nlattr *tb[IFLA_MAX + 1];
621         struct nlattr *pi[IFLA_INET6_MAX + 1];
622         int err;
623
624         ifi = nlmsg_data (hdr);
625         if (ifi->ifi_family != AF_INET6)
626                 return NULL;
627
628         device = nm_ip6_manager_get_device (manager, ifi->ifi_index);
629         if (!device || device->addrconf_complete)
630                 return NULL;
631
632         /* FIXME: we have to do this manually for now since libnl doesn't yet
633          * support the IFLA_PROTINFO attribute of NEWLINK messages.  When it does,
634          * we can get rid of this function and just grab IFLA_PROTINFO from
635          * nm_ip6_device_sync_from_netlink(), then get the IFLA_INET6_FLAGS out of
636          * the PROTINFO.
637          */
638
639         err = nlmsg_parse (hdr, sizeof (*ifi), tb, IFLA_MAX, link_policy);
640         if (err < 0)
641                 return NULL;
642         if (!tb[IFLA_PROTINFO])
643                 return NULL;
644
645         err = nla_parse_nested (pi, IFLA_INET6_MAX, tb[IFLA_PROTINFO], link_prot_policy);
646         if (err < 0)
647                 return NULL;
648         if (!pi[IFLA_INET6_FLAGS])
649                 return NULL;
650
651         device->ra_flags = nla_get_u32 (pi[IFLA_INET6_FLAGS]);
652         nm_log_dbg (LOGD_IP6, "(%s): got IPv6 flags 0x%X", device->iface, device->ra_flags);
653
654         return device;
655 }
656
657 static void
658 netlink_notification (NMNetlinkMonitor *monitor, struct nl_msg *msg, gpointer user_data)
659 {
660         NMIP6Manager *manager = (NMIP6Manager *) user_data;
661         NMIP6Device *device;
662         struct nlmsghdr *hdr;
663         gboolean config_changed = FALSE;
664
665         hdr = nlmsg_hdr (msg);
666         switch (hdr->nlmsg_type) {
667         case RTM_NEWADDR:
668         case RTM_DELADDR:
669                 device = process_addr (manager, msg);
670                 config_changed = TRUE;
671                 break;
672         case RTM_NEWROUTE:
673         case RTM_DELROUTE:
674                 device = process_route (manager, msg);
675                 config_changed = TRUE;
676                 break;
677         case RTM_NEWPREFIX:
678                 device = process_prefix (manager, msg);
679                 break;
680         case RTM_NEWNDUSEROPT:
681                 device = process_nduseropt (manager, msg);
682                 config_changed = TRUE;
683                 break;
684         case RTM_NEWLINK:
685                 device = process_newlink (manager, msg);
686                 config_changed = TRUE;
687                 break;
688         default:
689                 return;
690         }
691
692         if (device)
693                 nm_ip6_device_sync_from_netlink (device, config_changed);
694 }
695
696 void
697 nm_ip6_manager_prepare_interface (NMIP6Manager *manager,
698                                                                   int ifindex,
699                                                                   NMSettingIP6Config *s_ip6)
700 {
701         NMIP6ManagerPrivate *priv;
702         NMIP6Device *device;
703         const char *method = NULL;
704
705         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
706         g_return_if_fail (ifindex > 0);
707
708         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
709
710         device = nm_ip6_device_new (manager, ifindex);
711         g_return_if_fail (device != NULL);
712         g_return_if_fail (   strchr (device->iface, '/') == NULL
713                           && strcmp (device->iface, "all") != 0
714                           && strcmp (device->iface, "default") != 0);
715
716         if (s_ip6)
717                 method = nm_setting_ip6_config_get_method (s_ip6);
718         if (!method)
719                 method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
720
721         /* Establish target state and turn router advertisement acceptance on or off */
722         if (   !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)
723                 || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
724                 device->target_state = NM_IP6_DEVICE_GOT_LINK_LOCAL;
725                 nm_utils_do_sysctl (device->accept_ra_path, "0\n");
726         } else {
727                 device->target_state = NM_IP6_DEVICE_GOT_ADDRESS;
728                 nm_utils_do_sysctl (device->accept_ra_path, "1\n");
729         }
730 }
731
732 static gboolean
733 poll_ip6_flags (gpointer user_data)
734 {
735         nm_netlink_monitor_request_ip6_info (NM_NETLINK_MONITOR (user_data), NULL);
736         return TRUE;
737 }
738
739 void
740 nm_ip6_manager_begin_addrconf (NMIP6Manager *manager, int ifindex)
741 {
742         NMIP6ManagerPrivate *priv;
743         NMIP6Device *device;
744         CallbackInfo *info;
745
746         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
747         g_return_if_fail (ifindex > 0);
748
749         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
750
751         device = (NMIP6Device *) g_hash_table_lookup (priv->devices, GINT_TO_POINTER (ifindex));
752         g_return_if_fail (device != NULL);
753
754         nm_log_info (LOGD_IP6, "Activation (%s) Beginning IP6 addrconf.", device->iface);
755
756         device->addrconf_complete = FALSE;
757         device->ra_flags = 0;
758
759         /* Set up a timeout on the transaction to kill it after the timeout */
760         info = callback_info_new (device, 0, FALSE);
761         device->finish_addrconf_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
762                                                                  NM_IP6_TIMEOUT,
763                                                                  finish_addrconf,
764                                                                  info,
765                                                                  (GDestroyNotify) g_free);
766
767         /* Bounce IPv6 on the interface to ensure the kernel will start looking for
768          * new RAs; there doesn't seem to be a better way to do this right now.
769          */
770         if (device->target_state >= NM_IP6_DEVICE_GOT_ADDRESS) {
771                 nm_utils_do_sysctl (device->disable_ip6_path, "1\n");
772                 g_usleep (200);
773                 nm_utils_do_sysctl (device->disable_ip6_path, "0\n");
774         }
775
776         device->ip6flags_poll_id = g_timeout_add_seconds (1, poll_ip6_flags, priv->monitor);
777
778         /* Kick off the initial IPv6 flags request */
779         nm_netlink_monitor_request_ip6_info (priv->monitor, NULL);
780
781         /* Sync flags, etc, from netlink; this will also notice if the
782          * device is already fully configured and schedule the
783          * ADDRCONF_COMPLETE signal in that case.
784          */
785         nm_ip6_device_sync_from_netlink (device, FALSE);
786 }
787
788 void
789 nm_ip6_manager_cancel_addrconf (NMIP6Manager *manager, int ifindex)
790 {
791         g_return_if_fail (NM_IS_IP6_MANAGER (manager));
792         g_return_if_fail (ifindex > 0);
793
794         g_hash_table_remove (NM_IP6_MANAGER_GET_PRIVATE (manager)->devices,
795                              GINT_TO_POINTER (ifindex));
796 }
797
798 #define FIRST_ROUTE(m) ((struct rtnl_route *) nl_cache_get_first (m))
799 #define NEXT_ROUTE(m) ((struct rtnl_route *) nl_cache_get_next ((struct nl_object *) m))
800
801 #define FIRST_ADDR(m) ((struct rtnl_addr *) nl_cache_get_first (m))
802 #define NEXT_ADDR(m) ((struct rtnl_addr *) nl_cache_get_next ((struct nl_object *) m))
803
804 NMIP6Config *
805 nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
806 {
807         NMIP6ManagerPrivate *priv;
808         NMIP6Device *device;
809         NMIP6Config *config;
810         struct rtnl_addr *rtnladdr;
811         struct nl_addr *nladdr;
812         struct in6_addr *addr;
813         NMIP6Address *ip6addr;
814         struct rtnl_route *rtnlroute;
815         struct nl_addr *nldest, *nlgateway;
816         struct in6_addr *dest, *gateway;
817         gboolean defgw_set = FALSE;
818         struct in6_addr defgw;
819         uint32_t metric;
820         NMIP6Route *ip6route;
821         int i;
822
823         g_return_val_if_fail (NM_IS_IP6_MANAGER (manager), NULL);
824         g_return_val_if_fail (ifindex > 0, NULL);
825
826         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
827
828         device = (NMIP6Device *) g_hash_table_lookup (priv->devices,
829                                                       GINT_TO_POINTER (ifindex));
830         if (!device) {
831                 nm_log_warn (LOGD_IP6, "(%d): addrconf not started.", ifindex);
832                 return NULL;
833         }
834
835         config = nm_ip6_config_new ();
836         if (!config) {
837                 nm_log_err (LOGD_IP6, "(%s): out of memory creating IP6 config object.",
838                             device->iface);
839                 return NULL;
840         }
841
842         /* Make sure we refill the route and address caches, otherwise we won't get
843          * up-to-date information here since the netlink route/addr change messages
844          * may be lagging a bit.
845          */
846         nl_cache_refill (priv->nlh, priv->route_cache);
847         nl_cache_refill (priv->nlh, priv->addr_cache);
848
849         /* Add routes */
850         for (rtnlroute = FIRST_ROUTE (priv->route_cache); rtnlroute; rtnlroute = NEXT_ROUTE (rtnlroute)) {
851                 /* Make sure it's an IPv6 route for this device */
852                 if (rtnl_route_get_oif (rtnlroute) != device->ifindex)
853                         continue;
854                 if (rtnl_route_get_family (rtnlroute) != AF_INET6)
855                         continue;
856
857                 nldest = rtnl_route_get_dst (rtnlroute);
858                 if (!nldest || nl_addr_get_family (nldest) != AF_INET6)
859                         continue;
860                 dest = nl_addr_get_binary_addr (nldest);
861
862                 nlgateway = rtnl_route_get_gateway (rtnlroute);
863                 if (!nlgateway || nl_addr_get_family (nlgateway) != AF_INET6)
864                         continue;
865                 gateway = nl_addr_get_binary_addr (nlgateway);
866
867                 if (rtnl_route_get_dst_len (rtnlroute) == 0) {
868                         /* Default gateway route; don't add to normal routes but to each address */
869                         if (!defgw_set) {
870                                 memcpy (&defgw, gateway, sizeof (defgw));
871                                 defgw_set = TRUE;
872                         }
873                         continue;
874                 }
875
876                 ip6route = nm_ip6_route_new ();
877                 nm_ip6_route_set_dest (ip6route, dest);
878                 nm_ip6_route_set_prefix (ip6route, rtnl_route_get_dst_len (rtnlroute));
879                 nm_ip6_route_set_next_hop (ip6route, gateway);
880                 metric = rtnl_route_get_metric (rtnlroute, 1);
881                 if (metric != UINT_MAX)
882                         nm_ip6_route_set_metric (ip6route, metric);
883                 nm_ip6_config_take_route (config, ip6route);
884         }
885
886         /* Add addresses */
887         for (rtnladdr = FIRST_ADDR (priv->addr_cache); rtnladdr; rtnladdr = NEXT_ADDR (rtnladdr)) {
888                 if (rtnl_addr_get_ifindex (rtnladdr) != device->ifindex)
889                         continue;
890
891                 nladdr = rtnl_addr_get_local (rtnladdr);
892                 if (!nladdr || nl_addr_get_family (nladdr) != AF_INET6)
893                         continue;
894
895                 addr = nl_addr_get_binary_addr (nladdr);
896                 ip6addr = nm_ip6_address_new ();
897                 nm_ip6_address_set_prefix (ip6addr, rtnl_addr_get_prefixlen (rtnladdr));
898                 nm_ip6_address_set_address (ip6addr, addr);
899                 nm_ip6_config_take_address (config, ip6addr);
900                 if (defgw_set)
901                         nm_ip6_address_set_gateway (ip6addr, &defgw);
902         }
903
904         /* Add DNS servers */
905         if (device->rdnss_servers) {
906                 NMIP6RDNSS *rdnss = (NMIP6RDNSS *)(device->rdnss_servers->data);
907
908                 for (i = 0; i < device->rdnss_servers->len; i++)
909                         nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
910         }
911
912         return config;
913 }
914
915 /******************************************************************/
916
917 static NMIP6Manager *
918 nm_ip6_manager_new (void)
919 {
920         NMIP6Manager *manager;
921         NMIP6ManagerPrivate *priv;
922
923         manager = g_object_new (NM_TYPE_IP6_MANAGER, NULL);
924         priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
925
926         if (!priv->devices) {
927                 nm_log_err (LOGD_IP6, "not enough memory to initialize IP6 manager tables");
928                 g_object_unref (manager);
929                 manager = NULL;
930         }
931
932         return manager;
933 }
934
935 NMIP6Manager *
936 nm_ip6_manager_get (void)
937 {
938         static NMIP6Manager *singleton = NULL;
939
940         if (!singleton) {
941                 singleton = nm_ip6_manager_new ();
942                 g_assert (singleton);
943         } else
944                 g_object_ref (singleton);
945
946         return singleton;
947 }
948
949 static void
950 nm_ip6_manager_init (NMIP6Manager *manager)
951 {
952         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (manager);
953
954         priv->devices = g_hash_table_new_full (g_direct_hash, g_direct_equal,
955                                                NULL,
956                                                (GDestroyNotify) nm_ip6_device_destroy);
957
958         priv->monitor = nm_netlink_monitor_get ();
959         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_IFADDR, NULL);
960         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_IPV6_PREFIX, NULL);
961         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_ND_USEROPT, NULL);
962         nm_netlink_monitor_subscribe (priv->monitor, RTNLGRP_LINK, NULL);
963
964         g_signal_connect (priv->monitor, "notification",
965                           G_CALLBACK (netlink_notification), manager);
966
967         priv->nlh = nm_netlink_get_default_handle ();
968         priv->addr_cache = rtnl_addr_alloc_cache (priv->nlh);
969         priv->route_cache = rtnl_route_alloc_cache (priv->nlh);
970 }
971
972 static void
973 finalize (GObject *object)
974 {
975         NMIP6ManagerPrivate *priv = NM_IP6_MANAGER_GET_PRIVATE (object);
976
977         g_hash_table_destroy (priv->devices);
978         g_object_unref (priv->monitor);
979         nl_cache_free (priv->addr_cache);
980         nl_cache_free (priv->route_cache);
981
982         G_OBJECT_CLASS (nm_ip6_manager_parent_class)->finalize (object);
983 }
984
985 static void
986 nm_ip6_manager_class_init (NMIP6ManagerClass *manager_class)
987 {
988         GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
989
990         g_type_class_add_private (manager_class, sizeof (NMIP6ManagerPrivate));
991
992         /* virtual methods */
993         object_class->finalize = finalize;
994
995         /* signals */
996         signals[ADDRCONF_COMPLETE] =
997                 g_signal_new ("addrconf-complete",
998                                           G_OBJECT_CLASS_TYPE (object_class),
999                                           G_SIGNAL_RUN_FIRST,
1000                                           G_STRUCT_OFFSET (NMIP6ManagerClass, addrconf_complete),
1001                                           NULL, NULL,
1002                                           _nm_marshal_VOID__INT_UINT_BOOLEAN,
1003                                           G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_BOOLEAN);
1004
1005         signals[CONFIG_CHANGED] =
1006                 g_signal_new ("config-changed",
1007                                           G_OBJECT_CLASS_TYPE (object_class),
1008                                           G_SIGNAL_RUN_FIRST,
1009                                           G_STRUCT_OFFSET (NMIP6ManagerClass, config_changed),
1010                                           NULL, NULL,
1011                                           _nm_marshal_VOID__INT_UINT,
1012                                           G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
1013 }
1014