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