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