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