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