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