978e87d6aa126dec2471a3d237be7098db75f888
[NetworkManager.git] / src / nm-netlink-monitor.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
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 of the License, or
7  * (at your option) 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) 2005 - 2010 Red Hat, Inc.
19  * Copyright (C) 2005 - 2008 Novell, Inc.
20  * Copyright (C) 2005 Ray Strode
21  *
22  * Some code borrowed from HAL:  
23  *
24  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
25  * Copyright (C) 2004 Novell, Inc.
26  */
27
28 /* for struct ucred and LIBNL_NEEDS_ADDR_CACHING_WORKAROUND */
29 #include <config.h>
30
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <linux/types.h>
36 #include <linux/netlink.h>
37 #include <linux/rtnetlink.h>
38 #include <linux/if.h>
39 #include <linux/unistd.h>
40 #include <unistd.h>
41 #include <netlink/object-api.h>
42 #include <netlink/route/addr.h>
43 #include <netlink/route/rtnl.h>
44
45 #include <glib.h>
46 #include <glib/gi18n.h>
47
48 #include "nm-netlink-compat.h"
49 #include "nm-netlink-monitor.h"
50 #include "nm-logging.h"
51
52 #define EVENT_CONDITIONS      ((GIOCondition) (G_IO_IN | G_IO_PRI))
53 #define ERROR_CONDITIONS      ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
54 #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
55
56 #define NM_NETLINK_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
57                                            NM_TYPE_NETLINK_MONITOR, \
58                                            NMNetlinkMonitorPrivate))
59
60 typedef struct {
61         /* Async event listener connection */
62         struct nl_sock *nlh_event;
63         GIOChannel *      io_channel;
64         guint             event_id;
65
66         /* Sync/blocking request/response connection */
67         struct nl_sock *nlh_sync;
68         struct nl_cache * link_cache;
69
70         guint request_status_id;
71
72         GHashTable *subscriptions;
73 } NMNetlinkMonitorPrivate;
74
75 enum {
76         NOTIFICATION = 0,
77         CARRIER_ON,
78         CARRIER_OFF,
79         ERROR,
80         LAST_SIGNAL
81 };
82 static guint signals[LAST_SIGNAL] = { 0 };
83
84
85 G_DEFINE_TYPE (NMNetlinkMonitor, nm_netlink_monitor, G_TYPE_OBJECT);
86
87 static void
88 link_msg_handler (struct nl_object *obj, void *arg)
89 {
90         NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg);
91         GError *error;
92         struct rtnl_link *filter;
93         struct rtnl_link *link_obj;
94         guint flags;
95         guint ifidx;
96
97         filter = rtnl_link_alloc ();
98         if (!filter) {
99                 error = g_error_new (NM_NETLINK_MONITOR_ERROR,
100                                      NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
101                                      _("error processing netlink message: %s"),
102                                      nl_geterror (ENOMEM));
103                 g_signal_emit (self, signals[ERROR], 0, error);
104                 g_error_free (error);
105                 return;
106         }
107
108         /* Ensure it's a link object */
109         if (nl_object_match_filter (obj, OBJ_CAST (filter)) == 0) {
110                 rtnl_link_put (filter);
111                 return;
112         }
113
114         link_obj = (struct rtnl_link *) obj;
115         flags = rtnl_link_get_flags (link_obj);
116         ifidx = rtnl_link_get_ifindex (link_obj);
117
118         nm_log_dbg (LOGD_HW, "netlink link message: iface idx %d flags 0x%X", ifidx, flags);
119
120         /* IFF_LOWER_UP is the indicator of carrier status since kernel commit
121          * b00055aacdb172c05067612278ba27265fcd05ce in 2.6.17.
122          */
123         if (flags & IFF_LOWER_UP)
124                 g_signal_emit (self, signals[CARRIER_ON], 0, ifidx);
125         else
126                 g_signal_emit (self, signals[CARRIER_OFF], 0, ifidx);
127
128         rtnl_link_put (filter);
129 }
130
131 static int
132 event_msg_recv (struct nl_msg *msg, void *arg)
133 {
134         struct nl_sock *nlh = arg;
135         struct nlmsghdr *hdr = nlmsg_hdr (msg);
136         struct ucred *creds = nlmsg_get_creds (msg);
137         const struct sockaddr_nl *snl;
138         guint32 local_port;
139         gboolean accept_msg = FALSE;
140
141         /* Only messages sent from the kernel */
142         if (!creds || creds->uid != 0) {
143                 nm_log_dbg (LOGD_HW, "ignoring netlink message from UID %d",
144                             creds ? creds->uid : -1);
145                 return NL_SKIP;
146         }
147
148         snl = nlmsg_get_src (msg);
149         g_assert (snl);
150
151         /* Accept any messages from the kernel */
152         if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0)
153                 accept_msg = TRUE;
154
155         /* And any multicast message directed to our netlink PID, since multicast
156          * currently requires CAP_ADMIN to use.
157          */
158         local_port = nl_socket_get_local_port (nlh);
159         if ((hdr->nlmsg_pid == local_port) && snl->nl_groups)
160                 accept_msg = TRUE;
161
162         if (accept_msg == FALSE) {
163                 nm_log_dbg (LOGD_HW, "ignoring netlink message from PID %d (local PID %d, multicast %d)",
164                             hdr->nlmsg_pid,
165                             local_port,
166                             (hdr->nlmsg_flags & NLM_F_MULTI));
167                 return NL_SKIP;
168         }
169
170         return NL_OK;
171 }
172
173 static int
174 event_msg_ready (struct nl_msg *msg, void *arg)
175 {
176         NMNetlinkMonitor *self = NM_NETLINK_MONITOR (arg);
177
178         /* By the time the message gets here we've already checked the sender
179          * and we're sure it's safe to parse this message.
180          */
181
182         /* Let clients handle generic messages */
183         g_signal_emit (self, signals[NOTIFICATION], 0, msg);
184
185         /* Parse carrier messages */
186         nl_msg_parse (msg, &link_msg_handler, self);
187
188         return NL_OK;
189 }
190
191 static gboolean
192 event_handler (GIOChannel *channel,
193                GIOCondition io_condition,
194                gpointer user_data)
195 {
196         NMNetlinkMonitor *self = (NMNetlinkMonitor *) user_data;
197         NMNetlinkMonitorPrivate *priv;
198         GError *error = NULL;
199         int err;
200
201         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), TRUE);
202
203         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
204         g_return_val_if_fail (priv->event_id > 0, TRUE);
205
206         if (io_condition & ERROR_CONDITIONS) {
207                 const char *err_msg;
208                 int err_code = 0;
209                 socklen_t err_len = sizeof (err_code);
210
211                 /* Grab error information */     
212                 if (getsockopt (g_io_channel_unix_get_fd (channel), 
213                                                 SOL_SOCKET, SO_ERROR, (void *) &err_code, &err_len))
214                         err_msg = strerror (err_code);
215                 else
216                         err_msg = _("error occurred while waiting for data on socket");
217
218                 error = g_error_new (NM_NETLINK_MONITOR_ERROR,
219                                      NM_NETLINK_MONITOR_ERROR_WAITING_FOR_SOCKET_DATA,
220                                      "%s", err_msg);
221                 g_signal_emit (self, signals[ERROR], 0, error);
222                 g_error_free (error);
223                 return TRUE;
224         } else if (io_condition & DISCONNECT_CONDITIONS)
225                 return FALSE;
226
227         g_return_val_if_fail (!(io_condition & ~EVENT_CONDITIONS), FALSE);
228
229         /* Process the netlink messages */
230         if ((err = nl_recvmsgs_default (priv->nlh_event)) < 0) {
231                 error = g_error_new (NM_NETLINK_MONITOR_ERROR,
232                                      NM_NETLINK_MONITOR_ERROR_PROCESSING_MESSAGE,
233                                      _("error processing netlink message: %s"),
234                                      nl_geterror (err));
235                 g_signal_emit (self, signals[ERROR], 0, error);
236                 g_error_free (error);
237         }
238
239         return TRUE;
240 }
241
242 static gboolean
243 nlh_setup (struct nl_sock *nlh,
244            nl_recvmsg_msg_cb_t valid_func,
245            gpointer cb_data,
246            GError **error)
247 {
248         int err;
249
250         nl_socket_modify_cb (nlh, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, cb_data);
251
252         if (valid_func)
253                 nl_socket_modify_cb (nlh, NL_CB_VALID, NL_CB_CUSTOM, valid_func, cb_data);
254
255         if ((err = nl_connect (nlh, NETLINK_ROUTE)) < 0) {
256                 g_set_error (error, NM_NETLINK_MONITOR_ERROR,
257                              NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT,
258                              _("unable to connect to netlink for monitoring link status: %s"),
259                              nl_geterror (err));
260                 return FALSE;
261         }
262
263         /* Enable unix socket peer credentials which we use for verifying that the
264          * sender of the message is actually the kernel.
265          */
266         if (nl_socket_set_passcred (nlh, 1) < 0) {
267                 g_set_error (error, NM_NETLINK_MONITOR_ERROR,
268                              NM_NETLINK_MONITOR_ERROR_NETLINK_CONNECT,
269                              _("unable to enable netlink handle credential passing: %s"),
270                              nl_geterror (err));
271                 return FALSE;
272         }
273
274         return TRUE;
275 }
276
277 static gboolean
278 event_connection_setup (NMNetlinkMonitor *self, GError **error)
279 {
280         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
281         GError *channel_error = NULL;
282         GIOFlags channel_flags;
283         struct nl_cb *cb;
284         int fd;
285
286         g_return_val_if_fail (priv->io_channel == NULL, FALSE);
287
288         /* Set up the event listener connection */
289         cb = nl_cb_alloc (NL_CB_DEFAULT);
290         priv->nlh_event = nl_socket_alloc_cb (cb);
291         nl_cb_put (cb);
292         if (!priv->nlh_event) {
293                 g_set_error (error, NM_NETLINK_MONITOR_ERROR,
294                              NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE,
295                              _("unable to allocate netlink handle for monitoring link status: %s"),
296                              nl_geterror (ENOMEM));
297                 goto error;
298         }
299
300         if (!nlh_setup (priv->nlh_event, event_msg_ready, self, error))
301                 goto error;
302
303         nl_socket_disable_seq_check (priv->nlh_event);
304
305         /* Subscribe to the LINK group for internal carrier signals */
306         if (!nm_netlink_monitor_subscribe (self, RTNLGRP_LINK, error))
307                 goto error;
308
309         fd = nl_socket_get_fd (priv->nlh_event);
310         priv->io_channel = g_io_channel_unix_new (fd);
311
312         g_io_channel_set_encoding (priv->io_channel, NULL, &channel_error);
313         /* Encoding is NULL, so no conversion error can possibly occur */
314         g_assert (channel_error == NULL);
315
316         g_io_channel_set_close_on_unref (priv->io_channel, TRUE);
317         channel_flags = g_io_channel_get_flags (priv->io_channel);
318         channel_error = NULL;
319         g_io_channel_set_flags (priv->io_channel,
320                                 channel_flags | G_IO_FLAG_NONBLOCK,
321                                 &channel_error);
322         if (channel_error != NULL) {
323                 g_propagate_error (error, channel_error);
324                 goto error;
325         }
326
327         return TRUE;
328
329 error:
330         if (priv->io_channel)
331                 nm_netlink_monitor_close_connection (self);
332
333         if (priv->nlh_event) {
334                 nl_socket_free (priv->nlh_event);
335                 priv->nlh_event = NULL;
336         }
337
338         return FALSE;
339 }
340
341 static gboolean
342 sync_connection_setup (NMNetlinkMonitor *self, GError **error)
343 {
344         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
345         struct nl_cb *cb;
346 #ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND
347         struct nl_cache *addr_cache;
348 #endif
349         int err;
350
351         /* Set up the event listener connection */
352         cb = nl_cb_alloc (NL_CB_DEFAULT);
353         priv->nlh_sync = nl_socket_alloc_cb (cb);
354         nl_cb_put (cb);
355         if (!priv->nlh_sync) {
356                 g_set_error (error, NM_NETLINK_MONITOR_ERROR,
357                              NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_HANDLE,
358                              _("unable to allocate netlink handle for monitoring link status: %s"),
359                              nl_geterror (ENOMEM));
360                 goto error;
361         }
362
363         if (!nlh_setup (priv->nlh_sync, NULL, self, error))
364                 goto error;
365
366 #ifdef LIBNL_NEEDS_ADDR_CACHING_WORKAROUND
367         /* Work around apparent libnl bug; rtnl_addr requires that all
368          * addresses have the "peer" attribute set in order to be compared
369          * for equality, but this attribute is not normally set. As a
370          * result, most addresses will not compare as equal even to
371          * themselves, busting caching.
372          */
373         rtnl_addr_alloc_cache (priv->nlh_sync, &addr_cache);
374         nl_cache_get_ops (addr_cache)->co_obj_ops->oo_id_attrs &= ~0x80;
375         nl_cache_free (addr_cache);
376 #endif
377
378         err = rtnl_link_alloc_cache (priv->nlh_sync, &priv->link_cache);
379
380         if (err) {
381                 g_set_error (error, NM_NETLINK_MONITOR_ERROR,
382                              NM_NETLINK_MONITOR_ERROR_NETLINK_ALLOC_LINK_CACHE,
383                              _("unable to allocate netlink link cache for monitoring link status: %s"),
384                              nl_geterror (err));
385                 goto error;
386         }
387         nl_cache_mngt_provide (priv->link_cache);
388
389         return TRUE;
390
391 error:
392         if (priv->link_cache) {
393                 nl_cache_free (priv->link_cache);
394                 priv->link_cache = NULL;
395         }
396
397         if (priv->nlh_sync) {
398                 nl_socket_free (priv->nlh_sync);
399                 priv->nlh_sync = NULL;
400         }
401
402         return FALSE;
403 }
404
405 gboolean
406 nm_netlink_monitor_open_connection (NMNetlinkMonitor *self, GError **error)
407 {
408         g_return_val_if_fail (self != NULL, FALSE);
409         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
410
411         if (!event_connection_setup (self, error))
412                 return FALSE;
413
414         if (!sync_connection_setup (self, error))
415                 return FALSE;
416
417         return TRUE;
418 }
419
420 void
421 nm_netlink_monitor_close_connection (NMNetlinkMonitor  *self)
422 {
423         NMNetlinkMonitorPrivate *priv;
424
425         g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
426
427         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
428         g_return_if_fail (priv->io_channel != NULL);
429
430         if (priv->event_id)
431                 nm_netlink_monitor_detach (self);
432
433         g_io_channel_shutdown (priv->io_channel,
434                                TRUE /* flush pending data */,
435                                NULL);
436         g_io_channel_unref (priv->io_channel);
437         priv->io_channel = NULL;
438 }
439
440 void
441 nm_netlink_monitor_attach (NMNetlinkMonitor *self)
442 {
443         NMNetlinkMonitorPrivate *priv;
444
445         g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
446
447         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
448         g_return_if_fail (priv->nlh_event != NULL);
449         g_return_if_fail (priv->event_id == 0);
450
451         priv->event_id = g_io_add_watch (priv->io_channel,
452                                          (EVENT_CONDITIONS | ERROR_CONDITIONS | DISCONNECT_CONDITIONS),
453                                          event_handler, self);
454 }
455
456 void
457 nm_netlink_monitor_detach (NMNetlinkMonitor *self)
458 {
459         NMNetlinkMonitorPrivate *priv;
460
461         g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
462
463         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
464         g_return_if_fail (priv->event_id > 0);
465
466         g_source_remove (priv->event_id);
467         priv->event_id = 0;
468 }
469
470 static int
471 get_subs (NMNetlinkMonitor *self, int group)
472 {
473         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
474
475         return GPOINTER_TO_INT (g_hash_table_lookup (priv->subscriptions,
476                                                      GINT_TO_POINTER (group)));
477 }
478
479 static void
480 set_subs (NMNetlinkMonitor *self, int group, int new_subs)
481 {
482         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
483
484         g_hash_table_insert (priv->subscriptions,
485                              GINT_TO_POINTER (group),
486                              GINT_TO_POINTER (new_subs));
487 }
488
489 gboolean
490 nm_netlink_monitor_subscribe (NMNetlinkMonitor *self, int group, GError **error)
491 {
492         NMNetlinkMonitorPrivate *priv;
493         int subs, err;
494
495         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
496
497         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
498
499         if (!priv->nlh_event) {
500                 if (!nm_netlink_monitor_open_connection (self, error))
501                         return FALSE;
502         }
503
504         subs = get_subs (self, group) + 1;
505         if (subs == 1) {
506                 if ((err = nl_socket_add_membership (priv->nlh_event, group)) < 0) {
507                         g_set_error (error, NM_NETLINK_MONITOR_ERROR,
508                                      NM_NETLINK_MONITOR_ERROR_NETLINK_JOIN_GROUP,
509                                      _("unable to join netlink group: %s"),
510                                      nl_geterror (err));
511                         return FALSE;
512                 }
513         }
514
515         /* Update # of subscriptions for this group */
516         set_subs (self, group, subs);
517
518         return TRUE;
519 }
520
521 void
522 nm_netlink_monitor_unsubscribe (NMNetlinkMonitor *self, int group)
523 {
524         NMNetlinkMonitorPrivate *priv;
525         int subs;
526
527         g_return_if_fail (self != NULL);
528         g_return_if_fail (NM_IS_NETLINK_MONITOR (self));
529
530         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
531         g_return_if_fail (priv->nlh_event != NULL);
532
533         subs = get_subs (self, group) - 1;
534         if (subs == 0)
535                 nl_socket_drop_membership (priv->nlh_event, group);
536
537         /* Update # of subscriptions for this group */
538         set_subs (self, group, subs);
539 }
540
541 /***************************************************************/
542
543 gboolean
544 nm_netlink_monitor_request_ip6_info (NMNetlinkMonitor *self, GError **error)
545 {
546         NMNetlinkMonitorPrivate *priv;
547
548         g_return_val_if_fail (self != NULL, FALSE);
549         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
550
551         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
552
553         /* FIXME: nl_rtgen_request() gets the return value screwed up with
554          * libnl-1.1; revisit this and return a proper error when we port to
555          * a later libnl.
556          */
557         nl_rtgen_request (priv->nlh_event, RTM_GETLINK, AF_INET6, NLM_F_DUMP);
558
559         return TRUE;
560 }
561
562
563 static gboolean
564 deferred_emit_carrier_state (gpointer user_data)
565 {
566         NMNetlinkMonitor *self = NM_NETLINK_MONITOR (user_data);
567         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
568         int err;
569
570         priv->request_status_id = 0;
571
572         /* Update the link cache with latest state, and if there are no errors
573          * emit the link states for all the interfaces in the cache.
574          */
575         if ((err = nl_cache_refill (priv->nlh_sync, priv->link_cache)) != 0) {
576                 nm_log_err (LOGD_HW, "error updating link cache: %s", nl_geterror (err));
577         } else
578                 nl_cache_foreach_filter (priv->link_cache, NULL, link_msg_handler, self);
579
580         return FALSE;
581 }
582
583 gboolean
584 nm_netlink_monitor_request_status (NMNetlinkMonitor *self, GError **error)
585 {
586         NMNetlinkMonitorPrivate *priv;
587
588         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
589
590         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
591
592         /* Schedule the carrier state emission */
593         if (!priv->request_status_id)
594                 priv->request_status_id = g_idle_add (deferred_emit_carrier_state, self);
595
596         return TRUE;
597 }
598
599 typedef struct {
600         NMNetlinkMonitor *self;
601         struct rtnl_link *filter;
602         GError *error;
603         guint32 flags;
604 } GetFlagsInfo;
605
606 static void
607 get_flags_sync_cb (struct nl_object *obj, void *arg)
608 {
609         GetFlagsInfo *info = arg;
610
611         /* Ensure this cache item matches our filter */
612         if (nl_object_match_filter (obj, OBJ_CAST (info->filter)) != 0)
613                 info->flags = rtnl_link_get_flags ((struct rtnl_link *) obj);
614 }
615
616 gboolean
617 nm_netlink_monitor_get_flags_sync (NMNetlinkMonitor *self,
618                                    guint32 ifindex,
619                                    guint32 *ifflags,
620                                    GError **error)
621 {
622         NMNetlinkMonitorPrivate *priv;
623         GetFlagsInfo info;
624         struct rtnl_link *filter;
625         int err;
626
627         g_return_val_if_fail (self != NULL, FALSE);
628         g_return_val_if_fail (NM_IS_NETLINK_MONITOR (self), FALSE);
629         g_return_val_if_fail (ifflags != NULL, FALSE);
630
631         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
632
633         /* Update the link cache with the latest information */
634         if ((err = nl_cache_refill (priv->nlh_sync, priv->link_cache)) != 0) {
635                 g_set_error (error,
636                              NM_NETLINK_MONITOR_ERROR,
637                              NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
638                              _("error updating link cache: %s"),
639                              nl_geterror (err));
640                 return FALSE;
641         }
642
643         /* HACK: Apparently to get it working we have to refill the cache twice;
644          * otherwise some kernels (or maybe libnl?) only send a few of the
645          * interfaces in the refill request.
646          */
647         if (nl_cache_refill (priv->nlh_sync, priv->link_cache)) {
648                 g_set_error (error,
649                              NM_NETLINK_MONITOR_ERROR,
650                              NM_NETLINK_MONITOR_ERROR_LINK_CACHE_UPDATE,
651                              _("error updating link cache: %s"),
652                              nl_geterror (err));
653                 return FALSE;
654         }
655
656         /* Set up the filter */
657         filter = rtnl_link_alloc ();
658         if (!filter) {
659                 g_set_error (error,
660                              NM_NETLINK_MONITOR_ERROR,
661                              NM_NETLINK_MONITOR_ERROR_BAD_ALLOC,
662                              _("error processing netlink message: %s"),
663                              nl_geterror (err));
664                 return FALSE;
665         }
666         rtnl_link_set_ifindex (filter, ifindex);
667
668         memset (&info, 0, sizeof (info));
669         info.self = self;
670         info.filter = filter;
671         info.error = NULL;
672         nl_cache_foreach_filter (priv->link_cache, NULL, get_flags_sync_cb, &info);
673
674         rtnl_link_put (filter);
675
676         if (info.error) {
677                 if (error)
678                         *error = info.error;
679                 else
680                         g_error_free (info.error);
681                 return FALSE;
682         } else
683                 *ifflags = info.flags;
684
685         return TRUE; /* success */
686 }
687
688 /***************************************************************/
689
690 struct nl_sock *
691 nm_netlink_get_default_handle (void)
692 {
693         NMNetlinkMonitor *self;
694         struct nl_sock *nlh;
695
696         self = nm_netlink_monitor_get ();
697         nlh = NM_NETLINK_MONITOR_GET_PRIVATE (self)->nlh_sync;
698         g_object_unref (self);
699
700         return nlh;
701 }
702
703 int
704 nm_netlink_iface_to_index (const char *iface)
705 {
706         NMNetlinkMonitor *self;
707         NMNetlinkMonitorPrivate *priv;
708         int idx;
709
710         g_return_val_if_fail (iface != NULL, -1);
711
712         self = nm_netlink_monitor_get ();
713         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
714
715         nl_cache_refill (priv->nlh_sync, priv->link_cache);
716         idx = rtnl_link_name2i (priv->link_cache, iface);
717         g_object_unref (self);
718
719         return idx;
720 }
721
722 #define MAX_IFACE_LEN 33
723 char *
724 nm_netlink_index_to_iface (int idx)
725 {
726         NMNetlinkMonitor *self;
727         NMNetlinkMonitorPrivate *priv;
728         char *buf = NULL;
729
730         g_return_val_if_fail (idx >= 0, NULL);
731
732         self = nm_netlink_monitor_get ();
733         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
734
735         buf = g_malloc0 (MAX_IFACE_LEN);
736         g_assert (buf);
737
738         nl_cache_refill (priv->nlh_sync, priv->link_cache);
739         if (!rtnl_link_i2name (priv->link_cache, idx, buf, MAX_IFACE_LEN - 1)) {
740                 g_free (buf);
741                 buf = NULL;
742         }
743
744         g_object_unref (self);
745         return buf;
746 }
747
748 struct rtnl_link *
749 nm_netlink_index_to_rtnl_link (int idx)
750 {
751         NMNetlinkMonitor *self;
752         NMNetlinkMonitorPrivate *priv;
753         struct rtnl_link *ret = NULL;
754
755         if (idx <= 0)
756                 return NULL;
757
758         self = nm_netlink_monitor_get ();
759         priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
760
761         nl_cache_refill (priv->nlh_sync, priv->link_cache);
762         ret = rtnl_link_get (priv->link_cache, idx);
763         g_object_unref (self);
764
765         return ret;
766 }
767
768 /***************************************************************/
769
770 NMNetlinkMonitor *
771 nm_netlink_monitor_get (void)
772 {
773         static NMNetlinkMonitor *singleton = NULL;
774
775         if (!singleton)
776                 singleton = NM_NETLINK_MONITOR (g_object_new (NM_TYPE_NETLINK_MONITOR, NULL));
777         else
778                 g_object_ref (singleton);
779
780         return singleton;
781 }
782
783 static void
784 nm_netlink_monitor_init (NMNetlinkMonitor *self)
785 {
786         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (self);
787
788         priv->subscriptions = g_hash_table_new (g_direct_hash, g_direct_equal);
789 }
790
791 static void
792 finalize (GObject *object)
793 {
794         NMNetlinkMonitorPrivate *priv = NM_NETLINK_MONITOR_GET_PRIVATE (object);
795
796         if (priv->request_status_id)
797                 g_source_remove (priv->request_status_id);
798
799         if (priv->io_channel)
800                 nm_netlink_monitor_close_connection (NM_NETLINK_MONITOR (object));
801
802         if (priv->link_cache) {
803                 nl_cache_free (priv->link_cache);
804                 priv->link_cache = NULL;
805         }
806
807         if (priv->nlh_event) {
808                 nl_socket_free (priv->nlh_event);
809                 priv->nlh_event = NULL;
810         }
811
812         if (priv->nlh_sync) {
813                 nl_socket_free (priv->nlh_sync);
814                 priv->nlh_sync = NULL;
815         }
816
817         g_hash_table_destroy (priv->subscriptions);
818
819         G_OBJECT_CLASS (nm_netlink_monitor_parent_class)->finalize (object);
820 }
821
822 static void
823 nm_netlink_monitor_class_init (NMNetlinkMonitorClass *monitor_class)
824 {
825         GObjectClass *object_class = G_OBJECT_CLASS (monitor_class);
826
827         g_type_class_add_private (monitor_class, sizeof (NMNetlinkMonitorPrivate));
828
829         /* Virtual methods */
830         object_class->finalize = finalize;
831
832         /* Signals */
833         signals[NOTIFICATION] =
834                 g_signal_new ("notification",
835                               G_OBJECT_CLASS_TYPE (object_class),
836                               G_SIGNAL_RUN_LAST,
837                               G_STRUCT_OFFSET (NMNetlinkMonitorClass, notification),
838                               NULL, NULL, g_cclosure_marshal_VOID__POINTER,
839                               G_TYPE_NONE, 1, G_TYPE_POINTER);
840
841         signals[CARRIER_ON] =
842                 g_signal_new ("carrier-on",
843                               G_OBJECT_CLASS_TYPE (object_class),
844                               G_SIGNAL_RUN_LAST,
845                               G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_on),
846                               NULL, NULL, g_cclosure_marshal_VOID__INT,
847                               G_TYPE_NONE, 1, G_TYPE_INT);
848
849         signals[CARRIER_OFF] =
850                 g_signal_new ("carrier-off",
851                               G_OBJECT_CLASS_TYPE (object_class),
852                               G_SIGNAL_RUN_LAST,
853                               G_STRUCT_OFFSET (NMNetlinkMonitorClass, carrier_off),
854                               NULL, NULL, g_cclosure_marshal_VOID__INT,
855                               G_TYPE_NONE, 1, G_TYPE_INT);
856
857         signals[ERROR] =
858                 g_signal_new ("error",
859                               G_OBJECT_CLASS_TYPE (object_class),
860                               G_SIGNAL_RUN_LAST,
861                               G_STRUCT_OFFSET (NMNetlinkMonitorClass, error),
862                               NULL, NULL, g_cclosure_marshal_VOID__POINTER,
863                               G_TYPE_NONE, 1, G_TYPE_POINTER);
864 }
865
866 GQuark
867 nm_netlink_monitor_error_quark (void)
868 {
869         static GQuark error_quark = 0;
870
871         if (G_UNLIKELY (error_quark == 0))
872                 error_quark = g_quark_from_static_string ("nm-netlink-monitor-error-quark");
873         return error_quark;
874 }
875