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.
26 #include "gnetworkmonitornetlink.h"
27 #include "gcredentials.h"
28 #include "ginetaddressmask.h"
29 #include "ginitable.h"
30 #include "giomodule-priv.h"
32 #include "gnetworkingprivate.h"
33 #include "gnetworkmonitor.h"
35 #include "gunixcredentialsmessage.h"
37 /* must come at the end to pick system includes from
38 * gnetworkingprivate.h */
39 #include <linux/netlink.h>
40 #include <linux/rtnetlink.h>
42 static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
43 static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
45 #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
46 G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
47 G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
48 g_network_monitor_netlink_iface_init)
49 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
50 g_network_monitor_netlink_initable_iface_init)
51 _g_io_modules_ensure_extension_points_registered ();
52 g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
57 struct _GNetworkMonitorNetlinkPrivate
60 GSource *source, *dump_source;
62 GPtrArray *dump_networks;
65 static gboolean read_netlink_messages (GSocket *socket,
66 GIOCondition condition,
68 static gboolean request_dump (GNetworkMonitorNetlink *nl,
72 g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
74 nl->priv = G_TYPE_INSTANCE_GET_PRIVATE (nl,
75 G_TYPE_NETWORK_MONITOR_NETLINK,
76 GNetworkMonitorNetlinkPrivate);
81 g_network_monitor_netlink_initable_init (GInitable *initable,
82 GCancellable *cancellable,
85 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
87 struct sockaddr_nl snl;
89 /* We create the socket the old-school way because sockaddr_netlink
90 * can't be represented as a GSocketAddress
92 sockfd = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
96 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
97 _("Could not create network monitor: %s"),
102 snl.nl_family = AF_NETLINK;
103 snl.nl_pid = snl.nl_pad = 0;
104 snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
105 if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
108 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
109 _("Could not create network monitor: %s"),
115 nl->priv->sock = g_socket_new_from_fd (sockfd, error);
118 g_prefix_error (error, "%s", _("Could not create network monitor: "));
123 if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED,
127 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
128 _("Could not create network monitor: %s"),
133 /* Request the current state */
134 if (!request_dump (nl, error))
137 /* And read responses; since we haven't yet marked the socket
138 * non-blocking, each call will block until a message is received.
140 while (nl->priv->dump_networks)
142 if (!read_netlink_messages (NULL, G_IO_IN, nl))
146 g_socket_set_blocking (nl->priv->sock, FALSE);
147 nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
148 g_source_set_callback (nl->priv->source,
149 (GSourceFunc) read_netlink_messages, nl, NULL);
150 g_source_attach (nl->priv->source,
151 g_main_context_get_thread_default ());
157 request_dump (GNetworkMonitorNetlink *nl,
161 struct rtgenmsg *gen;
162 gchar buf[NLMSG_SPACE (sizeof (*gen))];
164 memset (buf, 0, sizeof (buf));
165 n = (struct nlmsghdr*) buf;
166 n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
167 n->nlmsg_type = RTM_GETROUTE;
168 n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
170 gen = NLMSG_DATA (n);
171 gen->rtgen_family = AF_UNSPEC;
173 if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
176 g_prefix_error (error, "%s", _("Could not get network status: "));
180 nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
185 timeout_request_dump (gpointer user_data)
187 GNetworkMonitorNetlink *nl = user_data;
189 g_source_destroy (nl->priv->dump_source);
190 g_source_unref (nl->priv->dump_source);
191 nl->priv->dump_source = NULL;
193 request_dump (nl, NULL);
199 queue_request_dump (GNetworkMonitorNetlink *nl)
201 if (nl->priv->dump_networks)
204 if (nl->priv->dump_source)
206 g_source_destroy (nl->priv->dump_source);
207 g_source_unref (nl->priv->dump_source);
210 nl->priv->dump_source = g_timeout_source_new (1000);
211 g_source_set_callback (nl->priv->dump_source,
212 (GSourceFunc) timeout_request_dump, nl, NULL);
213 g_source_attach (nl->priv->dump_source,
214 g_main_context_get_thread_default ());
218 add_network (GNetworkMonitorNetlink *nl,
219 GSocketFamily family,
224 GInetAddress *dest_addr;
225 GInetAddressMask *network;
228 dest_addr = g_inet_address_new_from_bytes (dest, family);
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);
239 g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
240 g_object_unref (network);
245 remove_network (GNetworkMonitorNetlink *nl,
246 GSocketFamily family,
251 GInetAddress *dest_addr;
252 GInetAddressMask *network;
255 dest_addr = g_inet_address_new_from_bytes (dest, family);
257 dest_addr = g_inet_address_new_any (family);
258 network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
259 g_object_unref (dest_addr);
260 g_return_if_fail (network != NULL);
262 if (nl->priv->dump_networks)
264 GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
267 for (i = 0; i < nl->priv->dump_networks->len; i++)
269 if (g_inet_address_mask_equal (network, dump_networks[i]))
270 g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
272 g_object_unref (network);
276 g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
277 g_object_unref (network);
282 finish_dump (GNetworkMonitorNetlink *nl)
284 g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
285 (GInetAddressMask **)nl->priv->dump_networks->pdata,
286 nl->priv->dump_networks->len);
287 g_ptr_array_free (nl->priv->dump_networks, TRUE);
288 nl->priv->dump_networks = NULL;
292 read_netlink_messages (GSocket *socket,
293 GIOCondition condition,
296 GNetworkMonitorNetlink *nl = user_data;
299 GSocketControlMessage **cmsgs = NULL;
300 gint num_cmsgs = 0, i, flags;
301 GError *error = NULL;
304 struct nlmsghdr *msg;
308 guint8 *dest, *gateway;
309 gboolean retval = TRUE;
314 flags = MSG_PEEK | MSG_TRUNC;
315 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
316 NULL, NULL, &flags, NULL, &error);
319 g_warning ("Error on netlink socket: %s", error->message);
320 g_error_free (error);
321 if (nl->priv->dump_networks)
326 iv.buffer = g_malloc (len);
328 len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
329 &cmsgs, &num_cmsgs, NULL, NULL, &error);
332 g_warning ("Error on netlink socket: %s", error->message);
333 g_error_free (error);
334 if (nl->priv->dump_networks)
339 if (num_cmsgs != 1 || !G_IS_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]))
342 creds = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (cmsgs[0]));
343 sender = g_credentials_get_unix_user (creds, NULL);
347 msg = (struct nlmsghdr *) iv.buffer;
348 for (; len > 0; msg = NLMSG_NEXT (msg, len))
350 if (!NLMSG_OK (msg, (size_t) len))
352 g_warning ("netlink message was truncated; shouldn't happen...");
357 switch (msg->nlmsg_type)
361 rtmsg = NLMSG_DATA (msg);
363 if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
365 if (rtmsg->rtm_type == RTN_UNREACHABLE)
368 attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
369 attr = RTM_RTA (rtmsg);
370 dest = gateway = NULL;
371 while (RTA_OK (attr, attrlen))
373 if (attr->rta_type == RTA_DST)
374 dest = RTA_DATA (attr);
375 else if (attr->rta_type == RTA_GATEWAY)
376 gateway = RTA_DATA (attr);
377 attr = RTA_NEXT (attr, attrlen);
382 if (msg->nlmsg_type == RTM_NEWROUTE)
383 add_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
385 remove_network (nl, rtmsg->rtm_family, rtmsg->rtm_dst_len, dest, gateway);
386 queue_request_dump (nl);
396 struct nlmsgerr *e = NLMSG_DATA (msg);
398 g_warning ("netlink error: %s", g_strerror (-e->error));
404 g_warning ("unexpected netlink message %d", msg->nlmsg_type);
411 for (i = 0; i < num_cmsgs; i++)
412 g_object_unref (cmsgs[i]);
417 if (!retval && nl->priv->dump_networks)
423 g_network_monitor_netlink_finalize (GObject *object)
425 GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
429 g_socket_close (nl->priv->sock, NULL);
430 g_object_unref (nl->priv->sock);
433 if (nl->priv->source)
435 g_source_destroy (nl->priv->source);
436 g_source_unref (nl->priv->source);
439 if (nl->priv->dump_source)
441 g_source_destroy (nl->priv->dump_source);
442 g_source_unref (nl->priv->dump_source);
445 G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
449 g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
451 GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
453 g_type_class_add_private (nl_class, sizeof (GNetworkMonitorNetlinkPrivate));
455 gobject_class->finalize = g_network_monitor_netlink_finalize;
459 g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
464 g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
466 iface->init = g_network_monitor_netlink_initable_init;