More clearly define 'named menu' in the XML parser
[glib.git] / gio / gnetworkmonitornetlink.c
blob046c9e92b165d4ac7912858fec0293365f9d3a36
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 <unistd.h>
25 #include <linux/netlink.h>
26 #include <linux/rtnetlink.h>
28 #include "gnetworkmonitornetlink.h"
29 #include "gcredentials.h"
30 #include "ginetaddressmask.h"
31 #include "ginitable.h"
32 #include "giomodule-priv.h"
33 #include "glibintl.h"
34 #include "gnetworkingprivate.h"
35 #include "gnetworkmonitor.h"
36 #include "gsocket.h"
37 #include "gunixcredentialsmessage.h"
39 static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
40 static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
42 #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
43 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
44 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
45 g_network_monitor_netlink_iface_init)
46 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
47 g_network_monitor_netlink_initable_iface_init)
48 _g_io_modules_ensure_extension_points_registered ();
49 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
50 g_define_type_id,
51 "netlink",
52 20))
54 struct _GNetworkMonitorNetlinkPrivate
56 GSocket *sock;
57 GSource *source, *dump_source;
59 GPtrArray *dump_networks;
62 static gboolean read_netlink_messages (GSocket *socket,
63 GIOCondition condition,
64 gpointer user_data);
65 static gboolean request_dump (GNetworkMonitorNetlink *nl,
66 GError **error);
68 static void
69 g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
71 nl->priv = G_TYPE_INSTANCE_GET_PRIVATE (nl,
72 G_TYPE_NETWORK_MONITOR_NETLINK,
73 GNetworkMonitorNetlinkPrivate);
77 static gboolean
78 g_network_monitor_netlink_initable_init (GInitable *initable,
79 GCancellable *cancellable,
80 GError **error)
82 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
83 gint sockfd, val;
84 struct sockaddr_nl snl;
86 /* We create the socket the old-school way because sockaddr_netlink
87 * can't be represented as a GSocketAddress
89 sockfd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
90 if (sockfd == -1)
92 int errsv = errno;
93 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
94 _("Could not create network monitor: %s"),
95 g_strerror (errno));
96 return FALSE;
99 snl.nl_family = AF_NETLINK;
100 snl.nl_pid = snl.nl_pad = 0;
101 snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
102 if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
104 int errsv = errno;
105 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
106 _("Could not create network monitor: %s"),
107 g_strerror (errno));
108 close (sockfd);
109 return FALSE;
112 val = 1;
113 if (setsockopt (sockfd, SOL_SOCKET, SO_PASSCRED, &val, sizeof (val)) != 0)
115 int errsv = errno;
116 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
117 _("Could not create network monitor: %s"),
118 g_strerror (errno));
119 close (sockfd);
120 return FALSE;
123 nl->priv->sock = g_socket_new_from_fd (sockfd, error);
124 if (error)
126 g_prefix_error (error, "%s", _("Could not create network monitor: "));
127 close (sockfd);
128 return FALSE;
131 /* Request the current state */
132 if (!request_dump (nl, error))
133 return FALSE;
135 /* And read responses; since we haven't yet marked the socket
136 * non-blocking, each call will block until a message is received.
138 while (nl->priv->dump_networks)
140 if (!read_netlink_messages (NULL, G_IO_IN, nl))
141 break;
144 g_socket_set_blocking (nl->priv->sock, FALSE);
145 nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
146 g_source_set_callback (nl->priv->source,
147 (GSourceFunc) read_netlink_messages, nl, NULL);
148 g_source_attach (nl->priv->source,
149 g_main_context_get_thread_default ());
151 return TRUE;
154 static gboolean
155 request_dump (GNetworkMonitorNetlink *nl,
156 GError **error)
158 struct nlmsghdr *n;
159 struct rtgenmsg *gen;
160 gchar buf[NLMSG_SPACE (sizeof (*gen))];
162 memset (buf, 0, sizeof (buf));
163 n = (struct nlmsghdr*) buf;
164 n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
165 n->nlmsg_type = RTM_GETROUTE;
166 n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
167 n->nlmsg_pid = 0;
168 gen = NLMSG_DATA (n);
169 gen->rtgen_family = AF_UNSPEC;
171 if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
172 NULL, error) < 0)
174 g_prefix_error (error, "%s", _("Could not get network status: "));
175 return FALSE;
178 nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
179 return TRUE;
182 static gboolean
183 timeout_request_dump (gpointer user_data)
185 GNetworkMonitorNetlink *nl = user_data;
187 g_source_destroy (nl->priv->dump_source);
188 g_source_unref (nl->priv->dump_source);
189 nl->priv->dump_source = NULL;
191 request_dump (nl, NULL);
193 return FALSE;
196 static void
197 queue_request_dump (GNetworkMonitorNetlink *nl)
199 if (nl->priv->dump_networks)
200 return;
202 if (nl->priv->dump_source)
204 g_source_destroy (nl->priv->dump_source);
205 g_source_unref (nl->priv->dump_source);
208 nl->priv->dump_source = g_timeout_source_new (1000);
209 g_source_set_callback (nl->priv->dump_source,
210 (GSourceFunc) timeout_request_dump, nl, NULL);
211 g_source_attach (nl->priv->dump_source,
212 g_main_context_get_thread_default ());
215 static void
216 add_network (GNetworkMonitorNetlink *nl,
217 GSocketFamily family,
218 gint dest_len,
219 guint8 *dest,
220 guint8 *gateway)
222 GInetAddress *dest_addr;
223 GInetAddressMask *network;
225 if (dest)
226 dest_addr = g_inet_address_new_from_bytes (dest, family);
227 else
228 dest_addr = g_inet_address_new_any (family);
229 network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
230 g_object_unref (dest_addr);
231 g_return_if_fail (network != NULL);
233 if (nl->priv->dump_networks)
234 g_ptr_array_add (nl->priv->dump_networks, network);
235 else
237 g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
238 g_object_unref (network);
242 static void
243 remove_network (GNetworkMonitorNetlink *nl,
244 GSocketFamily family,
245 gint dest_len,
246 guint8 *dest,
247 guint8 *gateway)
249 GInetAddress *dest_addr;
250 GInetAddressMask *network;
252 if (dest)
253 dest_addr = g_inet_address_new_from_bytes (dest, family);
254 else
255 dest_addr = g_inet_address_new_any (family);
256 network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
257 g_object_unref (dest_addr);
258 g_return_if_fail (network != NULL);
260 if (nl->priv->dump_networks)
262 GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
263 int i;
265 for (i = 0; i < nl->priv->dump_networks->len; i++)
267 if (g_inet_address_mask_equal (network, dump_networks[i]))
268 g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
270 g_object_unref (network);
272 else
274 g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
275 g_object_unref (network);
279 static void
280 finish_dump (GNetworkMonitorNetlink *nl)
282 g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
283 (GInetAddressMask **)nl->priv->dump_networks->pdata,
284 nl->priv->dump_networks->len);
285 g_ptr_array_free (nl->priv->dump_networks, FALSE);
286 nl->priv->dump_networks = NULL;
289 static gboolean
290 read_netlink_messages (GSocket *socket,
291 GIOCondition condition,
292 gpointer user_data)
294 GNetworkMonitorNetlink *nl = user_data;
295 GInputVector iv;
296 gsize len;
297 GSocketControlMessage **cmsgs = NULL;
298 gint num_cmsgs = 0, i, flags;
299 GError *error = NULL;
300 GCredentials *creds;
301 uid_t sender;
302 struct nlmsghdr *msg;
303 struct rtmsg *rtmsg;
304 struct rtattr *attr;
305 gsize attrlen;
306 guint8 *dest, *gateway;
307 gboolean retval = TRUE;
309 iv.buffer = NULL;
310 iv.size = 0;
312 flags = MSG_PEEK | MSG_TRUNC;
313 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
314 NULL, NULL, &flags, NULL, &error);
315 if (len < 0)
317 g_warning ("Error on netlink socket: %s", error->message);
318 g_error_free (error);
319 if (nl->priv->dump_networks)
320 finish_dump (nl);
321 return FALSE;
324 iv.buffer = g_malloc (len);
325 iv.size = len;
326 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
327 &cmsgs, &num_cmsgs, NULL, NULL, &error);
328 if (len < 0)
330 g_warning ("Error on netlink socket: %s", error->message);
331 g_error_free (error);
332 if (nl->priv->dump_networks)
333 finish_dump (nl);
334 return FALSE;
337 if (num_cmsgs != 1 || !G_IS_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]))
338 goto done;
340 creds = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]));
341 sender = g_credentials_get_unix_user (creds, NULL);
342 if (sender != 0)
343 goto done;
345 msg = (struct nlmsghdr *) iv.buffer;
346 for (; len > 0; msg = NLMSG_NEXT (msg, len))
348 if (!NLMSG_OK (msg, (size_t) len))
350 g_warning ("netlink message was truncated; shouldn't happen...");
351 retval = FALSE;
352 goto done;
355 switch (msg->nlmsg_type)
357 case RTM_NEWROUTE:
358 case RTM_DELROUTE:
359 rtmsg = NLMSG_DATA (msg);
361 if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
362 continue;
363 if (rtmsg->rtm_type == RTN_UNREACHABLE)
364 continue;
366 attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
367 attr = RTM_RTA (rtmsg);
368 dest = gateway = NULL;
369 while (RTA_OK (attr, attrlen))
371 if (attr->rta_type == RTA_DST)
372 dest = RTA_DATA (attr);
373 else if (attr->rta_type == RTA_GATEWAY)
374 gateway = RTA_DATA (attr);
375 attr = RTA_NEXT (attr, attrlen);
378 if (dest || gateway)
380 if (msg->nlmsg_type == RTM_NEWROUTE)
381 add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
382 else
383 remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
384 queue_request_dump (nl);
386 break;
388 case NLMSG_DONE:
389 finish_dump (nl);
390 goto done;
392 case NLMSG_ERROR:
394 struct nlmsgerr *e = NLMSG_DATA (msg);
396 g_warning ("netlink error: %s", g_strerror (-e->error));
398 retval = FALSE;
399 goto done;
401 default:
402 g_warning ("unexpected netlink message %d", msg->nlmsg_type);
403 retval = FALSE;
404 goto done;
408 done:
409 for (i = 0; i < num_cmsgs; i++)
410 g_object_unref (cmsgs[i]);
411 g_free (cmsgs);
413 g_free (iv.buffer);
415 if (!retval && nl->priv->dump_networks)
416 finish_dump (nl);
417 return retval;
420 static void
421 g_network_monitor_netlink_finalize (GObject *object)
423 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
425 if (nl->priv->sock)
427 g_socket_close (nl->priv->sock, NULL);
428 g_object_unref (nl->priv->sock);
431 if (nl->priv->source)
433 g_source_destroy (nl->priv->source);
434 g_source_unref (nl->priv->source);
437 if (nl->priv->dump_source)
439 g_source_destroy (nl->priv->dump_source);
440 g_source_unref (nl->priv->dump_source);
443 G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
446 static void
447 g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
449 GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
451 g_type_class_add_private (nl_class, sizeof (GNetworkMonitorNetlinkPrivate));
453 gobject_class->finalize = g_network_monitor_netlink_finalize;
456 static void
457 g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
461 static void
462 g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
464 iface->init = g_network_monitor_netlink_initable_init;