glib/tests: Fix non-debug build of slice test
[glib.git] / gio / gnetworkmonitornetlink.c
blob9ce9f889bba2a9098296c59012b380c09986f46d
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright 2011 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include "config.h"
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
27 #include "gnetworkmonitornetlink.h"
28 #include "gcredentials.h"
29 #include "ginetaddressmask.h"
30 #include "ginitable.h"
31 #include "giomodule-priv.h"
32 #include "glibintl.h"
33 #include "glib/gstdio.h"
34 #include "gnetworkingprivate.h"
35 #include "gnetworkmonitor.h"
36 #include "gsocket.h"
37 #include "gunixcredentialsmessage.h"
39 /* must come at the end to pick system includes from
40 * gnetworkingprivate.h */
41 #include <linux/netlink.h>
42 #include <linux/rtnetlink.h>
44 static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
45 static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
47 struct _GNetworkMonitorNetlinkPrivate
49 GSocket *sock;
50 GSource *source, *dump_source;
52 GPtrArray *dump_networks;
55 static gboolean read_netlink_messages (GSocket *socket,
56 GIOCondition condition,
57 gpointer user_data);
58 static gboolean request_dump (GNetworkMonitorNetlink *nl,
59 GError **error);
61 #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
62 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
63 G_ADD_PRIVATE (GNetworkMonitorNetlink)
64 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
65 g_network_monitor_netlink_iface_init)
66 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
67 g_network_monitor_netlink_initable_iface_init)
68 _g_io_modules_ensure_extension_points_registered ();
69 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
70 g_define_type_id,
71 "netlink",
72 20))
74 static void
75 g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
77 nl->priv = g_network_monitor_netlink_get_instance_private (nl);
81 static gboolean
82 g_network_monitor_netlink_initable_init (GInitable *initable,
83 GCancellable *cancellable,
84 GError **error)
86 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
87 gint sockfd;
88 struct sockaddr_nl snl;
90 /* We create the socket the old-school way because sockaddr_netlink
91 * can't be represented as a GSocketAddress
93 sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL);
94 if (sockfd == -1)
96 int errsv = errno;
97 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
98 _("Could not create network monitor: %s"),
99 g_strerror (errno));
100 return FALSE;
103 snl.nl_family = AF_NETLINK;
104 snl.nl_pid = snl.nl_pad = 0;
105 snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
106 if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
108 int errsv = errno;
109 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
110 _("Could not create network monitor: %s"),
111 g_strerror (errno));
112 (void) g_close (sockfd, NULL);
113 return FALSE;
116 nl->priv->sock = g_socket_new_from_fd (sockfd, error);
117 if (error)
119 g_prefix_error (error, "%s", _("Could not create network monitor: "));
120 (void) g_close (sockfd, NULL);
121 return FALSE;
124 if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED,
125 TRUE, NULL))
127 int errsv = errno;
128 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
129 _("Could not create network monitor: %s"),
130 g_strerror (errno));
131 return FALSE;
134 /* Request the current state */
135 if (!request_dump (nl, error))
136 return FALSE;
138 /* And read responses; since we haven't yet marked the socket
139 * non-blocking, each call will block until a message is received.
141 while (nl->priv->dump_networks)
143 if (!read_netlink_messages (NULL, G_IO_IN, nl))
144 break;
147 g_socket_set_blocking (nl->priv->sock, FALSE);
148 nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
149 g_source_set_callback (nl->priv->source,
150 (GSourceFunc) read_netlink_messages, nl, NULL);
151 g_source_attach (nl->priv->source,
152 g_main_context_get_thread_default ());
154 return TRUE;
157 static gboolean
158 request_dump (GNetworkMonitorNetlink *nl,
159 GError **error)
161 struct nlmsghdr *n;
162 struct rtgenmsg *gen;
163 gchar buf[NLMSG_SPACE (sizeof (*gen))];
165 memset (buf, 0, sizeof (buf));
166 n = (struct nlmsghdr*) buf;
167 n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
168 n->nlmsg_type = RTM_GETROUTE;
169 n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
170 n->nlmsg_pid = 0;
171 gen = NLMSG_DATA (n);
172 gen->rtgen_family = AF_UNSPEC;
174 if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
175 NULL, error) < 0)
177 g_prefix_error (error, "%s", _("Could not get network status: "));
178 return FALSE;
181 nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
182 return TRUE;
185 static gboolean
186 timeout_request_dump (gpointer user_data)
188 GNetworkMonitorNetlink *nl = user_data;
190 g_source_destroy (nl->priv->dump_source);
191 g_source_unref (nl->priv->dump_source);
192 nl->priv->dump_source = NULL;
194 request_dump (nl, NULL);
196 return FALSE;
199 static void
200 queue_request_dump (GNetworkMonitorNetlink *nl)
202 if (nl->priv->dump_networks)
203 return;
205 if (nl->priv->dump_source)
207 g_source_destroy (nl->priv->dump_source);
208 g_source_unref (nl->priv->dump_source);
211 nl->priv->dump_source = g_timeout_source_new (1000);
212 g_source_set_callback (nl->priv->dump_source,
213 (GSourceFunc) timeout_request_dump, nl, NULL);
214 g_source_attach (nl->priv->dump_source,
215 g_main_context_get_thread_default ());
218 static void
219 add_network (GNetworkMonitorNetlink *nl,
220 GSocketFamily family,
221 gint dest_len,
222 guint8 *dest)
224 GInetAddress *dest_addr;
225 GInetAddressMask *network;
227 if (dest)
228 dest_addr = g_inet_address_new_from_bytes (dest, family);
229 else
230 dest_addr = g_inet_address_new_any (family);
231 network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
232 g_object_unref (dest_addr);
233 g_return_if_fail (network != NULL);
235 if (nl->priv->dump_networks)
236 g_ptr_array_add (nl->priv->dump_networks, network);
237 else
239 g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
240 g_object_unref (network);
244 static void
245 remove_network (GNetworkMonitorNetlink *nl,
246 GSocketFamily family,
247 gint dest_len,
248 guint8 *dest)
250 GInetAddress *dest_addr;
251 GInetAddressMask *network;
253 if (dest)
254 dest_addr = g_inet_address_new_from_bytes (dest, family);
255 else
256 dest_addr = g_inet_address_new_any (family);
257 network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
258 g_object_unref (dest_addr);
259 g_return_if_fail (network != NULL);
261 if (nl->priv->dump_networks)
263 GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
264 int i;
266 for (i = 0; i < nl->priv->dump_networks->len; i++)
268 if (g_inet_address_mask_equal (network, dump_networks[i]))
269 g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
271 g_object_unref (network);
273 else
275 g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
276 g_object_unref (network);
280 static void
281 finish_dump (GNetworkMonitorNetlink *nl)
283 g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
284 (GInetAddressMask **)nl->priv->dump_networks->pdata,
285 nl->priv->dump_networks->len);
286 g_ptr_array_free (nl->priv->dump_networks, TRUE);
287 nl->priv->dump_networks = NULL;
290 static gboolean
291 read_netlink_messages (GSocket *socket,
292 GIOCondition condition,
293 gpointer user_data)
295 GNetworkMonitorNetlink *nl = user_data;
296 GInputVector iv;
297 gssize len;
298 GSocketControlMessage **cmsgs = NULL;
299 gint num_cmsgs = 0, i, flags;
300 GError *error = NULL;
301 GCredentials *creds;
302 uid_t sender;
303 struct nlmsghdr *msg;
304 struct rtmsg *rtmsg;
305 struct rtattr *attr;
306 gsize attrlen;
307 guint8 *dest, *gateway, *oif;
308 gboolean retval = TRUE;
310 iv.buffer = NULL;
311 iv.size = 0;
313 flags = MSG_PEEK | MSG_TRUNC;
314 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
315 NULL, NULL, &flags, NULL, &error);
316 if (len < 0)
318 g_warning ("Error on netlink socket: %s", error->message);
319 g_error_free (error);
320 if (nl->priv->dump_networks)
321 finish_dump (nl);
322 return FALSE;
325 iv.buffer = g_malloc (len);
326 iv.size = len;
327 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
328 &cmsgs, &num_cmsgs, NULL, NULL, &error);
329 if (len < 0)
331 g_warning ("Error on netlink socket: %s", error->message);
332 g_error_free (error);
333 if (nl->priv->dump_networks)
334 finish_dump (nl);
335 return FALSE;
338 if (num_cmsgs != 1 || !G_IS_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]))
339 goto done;
341 creds = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]));
342 sender = g_credentials_get_unix_user (creds, NULL);
343 if (sender != 0)
344 goto done;
346 msg = (struct nlmsghdr *) iv.buffer;
347 for (; len > 0; msg = NLMSG_NEXT (msg, len))
349 if (!NLMSG_OK (msg, (size_t) len))
351 g_warning ("netlink message was truncated; shouldn't happen...");
352 retval = FALSE;
353 goto done;
356 switch (msg->nlmsg_type)
358 case RTM_NEWROUTE:
359 case RTM_DELROUTE:
360 rtmsg = NLMSG_DATA (msg);
362 if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
363 continue;
364 if (rtmsg->rtm_type == RTN_UNREACHABLE)
365 continue;
367 attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
368 attr = RTM_RTA (rtmsg);
369 dest = gateway = oif = NULL;
370 while (RTA_OK (attr, attrlen))
372 if (attr->rta_type == RTA_DST)
373 dest = RTA_DATA (attr);
374 else if (attr->rta_type == RTA_GATEWAY)
375 gateway = RTA_DATA (attr);
376 else if (attr->rta_type == RTA_OIF)
377 oif = RTA_DATA (attr);
378 attr = RTA_NEXT (attr, attrlen);
381 if (dest || gateway || oif)
383 if (msg->nlmsg_type == RTM_NEWROUTE)
384 add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest);
385 else
386 remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest);
387 queue_request_dump (nl);
389 break;
391 case NLMSG_DONE:
392 finish_dump (nl);
393 goto done;
395 case NLMSG_ERROR:
397 struct nlmsgerr *e = NLMSG_DATA (msg);
399 g_warning ("netlink error: %s", g_strerror (-e->error));
401 retval = FALSE;
402 goto done;
404 default:
405 g_warning ("unexpected netlink message %d", msg->nlmsg_type);
406 retval = FALSE;
407 goto done;
411 done:
412 for (i = 0; i < num_cmsgs; i++)
413 g_object_unref (cmsgs[i]);
414 g_free (cmsgs);
416 g_free (iv.buffer);
418 if (!retval && nl->priv->dump_networks)
419 finish_dump (nl);
420 return retval;
423 static void
424 g_network_monitor_netlink_finalize (GObject *object)
426 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
428 if (nl->priv->sock)
430 g_socket_close (nl->priv->sock, NULL);
431 g_object_unref (nl->priv->sock);
434 if (nl->priv->source)
436 g_source_destroy (nl->priv->source);
437 g_source_unref (nl->priv->source);
440 if (nl->priv->dump_source)
442 g_source_destroy (nl->priv->dump_source);
443 g_source_unref (nl->priv->dump_source);
446 G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
449 static void
450 g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
452 GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
454 gobject_class->finalize = g_network_monitor_netlink_finalize;
457 static void
458 g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
462 static void
463 g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
465 iface->init = g_network_monitor_netlink_initable_init;