/src/glib/gio/gnetworkmonitornetlink.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GIO - GLib Input, Output and Streaming Library |
2 | | * |
3 | | * Copyright 2011 Red Hat, Inc. |
4 | | * |
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.1 of the License, or (at your option) any later version. |
9 | | * |
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. |
14 | | * |
15 | | * You should have received a copy of the GNU Lesser General |
16 | | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | |
21 | | #include <errno.h> |
22 | | #include <string.h> |
23 | | #include <unistd.h> |
24 | | |
25 | | #include "gnetworkmonitornetlink.h" |
26 | | #include "gcredentials.h" |
27 | | #include "ginetaddressmask.h" |
28 | | #include "ginitable.h" |
29 | | #include "giomodule-priv.h" |
30 | | #include "glibintl.h" |
31 | | #include "glib/gstdio.h" |
32 | | #include "gnetworkingprivate.h" |
33 | | #include "gnetworkmonitor.h" |
34 | | #include "gsocket.h" |
35 | | #include "gunixcredentialsmessage.h" |
36 | | |
37 | | /* must come at the end to pick system includes from |
38 | | * gnetworkingprivate.h */ |
39 | | #include <linux/netlink.h> |
40 | | #include <linux/rtnetlink.h> |
41 | | |
42 | | static GInitableIface *initable_parent_iface; |
43 | | static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface); |
44 | | static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface); |
45 | | |
46 | | struct _GNetworkMonitorNetlinkPrivate |
47 | | { |
48 | | GSocket *sock; |
49 | | GSource *source, *dump_source; |
50 | | GMainContext *context; |
51 | | |
52 | | GPtrArray *dump_networks; |
53 | | }; |
54 | | |
55 | | static gboolean read_netlink_messages (GNetworkMonitorNetlink *nl, |
56 | | GError **error); |
57 | | static gboolean read_netlink_messages_callback (GSocket *socket, |
58 | | GIOCondition condition, |
59 | | gpointer user_data); |
60 | | static gboolean request_dump (GNetworkMonitorNetlink *nl, |
61 | | GError **error); |
62 | | |
63 | | #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type |
64 | | G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE, |
65 | | G_ADD_PRIVATE (GNetworkMonitorNetlink) |
66 | | G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR, |
67 | | g_network_monitor_netlink_iface_init) |
68 | | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, |
69 | | g_network_monitor_netlink_initable_iface_init) |
70 | | _g_io_modules_ensure_extension_points_registered (); |
71 | | g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME, |
72 | | g_define_type_id, |
73 | | "netlink", |
74 | | 20)) |
75 | | |
76 | | static void |
77 | | g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl) |
78 | 0 | { |
79 | 0 | nl->priv = g_network_monitor_netlink_get_instance_private (nl); |
80 | 0 | } |
81 | | |
82 | | static gboolean |
83 | | g_network_monitor_netlink_initable_init (GInitable *initable, |
84 | | GCancellable *cancellable, |
85 | | GError **error) |
86 | 0 | { |
87 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable); |
88 | 0 | gint sockfd; |
89 | 0 | struct sockaddr_nl snl; |
90 | | |
91 | | /* We create the socket the old-school way because sockaddr_netlink |
92 | | * can't be represented as a GSocketAddress |
93 | | */ |
94 | 0 | sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL); |
95 | 0 | if (sockfd == -1) |
96 | 0 | { |
97 | 0 | int errsv = errno; |
98 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
99 | 0 | _("Could not create network monitor: %s"), |
100 | 0 | g_strerror (errsv)); |
101 | 0 | return FALSE; |
102 | 0 | } |
103 | | |
104 | 0 | snl.nl_family = AF_NETLINK; |
105 | 0 | snl.nl_pid = snl.nl_pad = 0; |
106 | 0 | snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE; |
107 | 0 | if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0) |
108 | 0 | { |
109 | 0 | int errsv = errno; |
110 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
111 | 0 | _("Could not create network monitor: %s"), |
112 | 0 | g_strerror (errsv)); |
113 | 0 | (void) g_close (sockfd, NULL); |
114 | 0 | return FALSE; |
115 | 0 | } |
116 | | |
117 | 0 | nl->priv->sock = g_socket_new_from_fd (sockfd, error); |
118 | 0 | if (!nl->priv->sock) |
119 | 0 | { |
120 | 0 | g_prefix_error (error, "%s", _("Could not create network monitor: ")); |
121 | 0 | (void) g_close (sockfd, NULL); |
122 | 0 | return FALSE; |
123 | 0 | } |
124 | | |
125 | 0 | if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED, |
126 | 0 | TRUE, NULL)) |
127 | 0 | { |
128 | 0 | int errsv = errno; |
129 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
130 | 0 | _("Could not create network monitor: %s"), |
131 | 0 | g_strerror (errsv)); |
132 | 0 | return FALSE; |
133 | 0 | } |
134 | | |
135 | | /* Request the current state */ |
136 | 0 | if (!request_dump (nl, error)) |
137 | 0 | return FALSE; |
138 | | |
139 | | /* And read responses; since we haven't yet marked the socket |
140 | | * non-blocking, each call will block until a message is received. |
141 | | */ |
142 | 0 | while (nl->priv->dump_networks) |
143 | 0 | { |
144 | 0 | GError *local_error = NULL; |
145 | 0 | if (!read_netlink_messages (nl, &local_error)) |
146 | 0 | { |
147 | 0 | g_warning ("%s", local_error->message); |
148 | 0 | g_clear_error (&local_error); |
149 | 0 | break; |
150 | 0 | } |
151 | 0 | } |
152 | |
|
153 | 0 | g_socket_set_blocking (nl->priv->sock, FALSE); |
154 | 0 | nl->priv->context = g_main_context_ref_thread_default (); |
155 | 0 | nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL); |
156 | 0 | g_source_set_callback (nl->priv->source, |
157 | 0 | (GSourceFunc) read_netlink_messages_callback, nl, NULL); |
158 | 0 | g_source_attach (nl->priv->source, nl->priv->context); |
159 | |
|
160 | 0 | return initable_parent_iface->init (initable, cancellable, error); |
161 | 0 | } |
162 | | |
163 | | static gboolean |
164 | | request_dump (GNetworkMonitorNetlink *nl, |
165 | | GError **error) |
166 | 0 | { |
167 | 0 | struct nlmsghdr *n; |
168 | 0 | struct rtgenmsg *gen; |
169 | 0 | gchar buf[NLMSG_SPACE (sizeof (*gen))]; |
170 | |
|
171 | 0 | memset (buf, 0, sizeof (buf)); |
172 | 0 | n = (struct nlmsghdr*) buf; |
173 | 0 | n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen)); |
174 | 0 | n->nlmsg_type = RTM_GETROUTE; |
175 | 0 | n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
176 | 0 | n->nlmsg_pid = 0; |
177 | 0 | gen = NLMSG_DATA (n); |
178 | 0 | gen->rtgen_family = AF_UNSPEC; |
179 | |
|
180 | 0 | if (g_socket_send (nl->priv->sock, buf, sizeof (buf), |
181 | 0 | NULL, error) < 0) |
182 | 0 | { |
183 | 0 | g_prefix_error (error, "%s", _("Could not get network status: ")); |
184 | 0 | return FALSE; |
185 | 0 | } |
186 | | |
187 | 0 | nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref); |
188 | 0 | return TRUE; |
189 | 0 | } |
190 | | |
191 | | static gboolean |
192 | | timeout_request_dump (gpointer user_data) |
193 | 0 | { |
194 | 0 | GNetworkMonitorNetlink *nl = user_data; |
195 | |
|
196 | 0 | g_source_destroy (nl->priv->dump_source); |
197 | 0 | g_source_unref (nl->priv->dump_source); |
198 | 0 | nl->priv->dump_source = NULL; |
199 | |
|
200 | 0 | request_dump (nl, NULL); |
201 | |
|
202 | 0 | return FALSE; |
203 | 0 | } |
204 | | |
205 | | static void |
206 | | queue_request_dump (GNetworkMonitorNetlink *nl) |
207 | 0 | { |
208 | 0 | if (nl->priv->dump_networks) |
209 | 0 | return; |
210 | | |
211 | 0 | if (nl->priv->dump_source) |
212 | 0 | { |
213 | 0 | g_source_destroy (nl->priv->dump_source); |
214 | 0 | g_source_unref (nl->priv->dump_source); |
215 | 0 | } |
216 | |
|
217 | 0 | nl->priv->dump_source = g_timeout_source_new_seconds (1); |
218 | 0 | g_source_set_callback (nl->priv->dump_source, |
219 | 0 | (GSourceFunc) timeout_request_dump, nl, NULL); |
220 | 0 | g_source_attach (nl->priv->dump_source, nl->priv->context); |
221 | 0 | } |
222 | | |
223 | | static GInetAddressMask * |
224 | | create_inet_address_mask (GSocketFamily family, |
225 | | const guint8 *dest, |
226 | | gsize dest_len) |
227 | 0 | { |
228 | 0 | GInetAddress *dest_addr; |
229 | 0 | GInetAddressMask *network; |
230 | |
|
231 | 0 | if (dest) |
232 | 0 | dest_addr = g_inet_address_new_from_bytes (dest, family); |
233 | 0 | else |
234 | 0 | dest_addr = g_inet_address_new_any (family); |
235 | 0 | network = g_inet_address_mask_new (dest_addr, dest_len, NULL); |
236 | 0 | g_object_unref (dest_addr); |
237 | |
|
238 | 0 | return network; |
239 | 0 | } |
240 | | |
241 | | static void |
242 | | add_network (GNetworkMonitorNetlink *nl, |
243 | | GSocketFamily family, |
244 | | const guint8 *dest, |
245 | | gsize dest_len) |
246 | 0 | { |
247 | 0 | GInetAddressMask *network = create_inet_address_mask (family, dest, dest_len); |
248 | 0 | g_return_if_fail (network != NULL); |
249 | | |
250 | 0 | if (nl->priv->dump_networks) |
251 | 0 | g_ptr_array_add (nl->priv->dump_networks, g_object_ref (network)); |
252 | 0 | else |
253 | 0 | g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network); |
254 | |
|
255 | 0 | g_object_unref (network); |
256 | 0 | } |
257 | | |
258 | | static void |
259 | | remove_network (GNetworkMonitorNetlink *nl, |
260 | | GSocketFamily family, |
261 | | const guint8 *dest, |
262 | | gsize dest_len) |
263 | 0 | { |
264 | 0 | GInetAddressMask *network = create_inet_address_mask (family, dest, dest_len); |
265 | 0 | g_return_if_fail (network != NULL); |
266 | | |
267 | 0 | if (nl->priv->dump_networks) |
268 | 0 | { |
269 | 0 | GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata; |
270 | 0 | guint i; |
271 | |
|
272 | 0 | for (i = 0; i < nl->priv->dump_networks->len; i++) |
273 | 0 | { |
274 | 0 | if (g_inet_address_mask_equal (network, dump_networks[i])) |
275 | 0 | g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--); |
276 | 0 | } |
277 | 0 | } |
278 | 0 | else |
279 | 0 | { |
280 | 0 | g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network); |
281 | 0 | } |
282 | |
|
283 | 0 | g_object_unref (network); |
284 | 0 | } |
285 | | |
286 | | static void |
287 | | finish_dump (GNetworkMonitorNetlink *nl) |
288 | 0 | { |
289 | 0 | g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl), |
290 | 0 | (GInetAddressMask **)nl->priv->dump_networks->pdata, |
291 | 0 | nl->priv->dump_networks->len); |
292 | 0 | g_ptr_array_free (nl->priv->dump_networks, TRUE); |
293 | 0 | nl->priv->dump_networks = NULL; |
294 | 0 | } |
295 | | |
296 | | static gboolean |
297 | | read_netlink_messages (GNetworkMonitorNetlink *nl, |
298 | | GError **error) |
299 | 0 | { |
300 | 0 | GInputVector iv; |
301 | 0 | gssize len; |
302 | 0 | gint flags; |
303 | 0 | GError *local_error = NULL; |
304 | 0 | GSocketAddress *addr = NULL; |
305 | 0 | struct nlmsghdr *msg; |
306 | 0 | struct rtmsg *rtmsg; |
307 | 0 | struct rtattr *attr; |
308 | 0 | struct sockaddr_nl source_sockaddr; |
309 | 0 | gsize attrlen; |
310 | 0 | guint8 *dest, *gateway, *oif; |
311 | 0 | gboolean retval = TRUE; |
312 | |
|
313 | 0 | iv.buffer = NULL; |
314 | 0 | iv.size = 0; |
315 | |
|
316 | 0 | flags = MSG_PEEK | MSG_TRUNC; |
317 | 0 | len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1, |
318 | 0 | NULL, NULL, &flags, NULL, &local_error); |
319 | 0 | if (len < 0) |
320 | 0 | { |
321 | 0 | retval = FALSE; |
322 | 0 | goto done; |
323 | 0 | } |
324 | | |
325 | 0 | iv.buffer = g_malloc (len); |
326 | 0 | iv.size = len; |
327 | 0 | len = g_socket_receive_message (nl->priv->sock, &addr, &iv, 1, |
328 | 0 | NULL, NULL, NULL, NULL, &local_error); |
329 | 0 | if (len < 0) |
330 | 0 | { |
331 | 0 | retval = FALSE; |
332 | 0 | goto done; |
333 | 0 | } |
334 | | |
335 | 0 | if (!g_socket_address_to_native (addr, &source_sockaddr, sizeof (source_sockaddr), &local_error)) |
336 | 0 | { |
337 | 0 | retval = FALSE; |
338 | 0 | goto done; |
339 | 0 | } |
340 | | |
341 | | /* If the sender port id is 0 (not fakeable) then the message is from the kernel */ |
342 | 0 | if (source_sockaddr.nl_pid != 0) |
343 | 0 | goto done; |
344 | | |
345 | 0 | msg = (struct nlmsghdr *) iv.buffer; |
346 | 0 | for (; len > 0; msg = NLMSG_NEXT (msg, len)) |
347 | 0 | { |
348 | 0 | if (!NLMSG_OK (msg, (size_t) len)) |
349 | 0 | { |
350 | 0 | g_set_error_literal (&local_error, |
351 | 0 | G_IO_ERROR, |
352 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
353 | 0 | "netlink message was truncated; shouldn't happen..."); |
354 | 0 | retval = FALSE; |
355 | 0 | goto done; |
356 | 0 | } |
357 | | |
358 | 0 | switch (msg->nlmsg_type) |
359 | 0 | { |
360 | 0 | case RTM_NEWROUTE: |
361 | 0 | case RTM_DELROUTE: |
362 | 0 | rtmsg = NLMSG_DATA (msg); |
363 | |
|
364 | 0 | if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6) |
365 | 0 | continue; |
366 | 0 | if (rtmsg->rtm_type == RTN_UNREACHABLE) |
367 | 0 | continue; |
368 | | |
369 | 0 | attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg)); |
370 | 0 | attr = RTM_RTA (rtmsg); |
371 | 0 | dest = gateway = oif = NULL; |
372 | 0 | while (RTA_OK (attr, attrlen)) |
373 | 0 | { |
374 | 0 | if (attr->rta_type == RTA_DST) |
375 | 0 | dest = RTA_DATA (attr); |
376 | 0 | else if (attr->rta_type == RTA_GATEWAY) |
377 | 0 | gateway = RTA_DATA (attr); |
378 | 0 | else if (attr->rta_type == RTA_OIF) |
379 | 0 | oif = RTA_DATA (attr); |
380 | 0 | attr = RTA_NEXT (attr, attrlen); |
381 | 0 | } |
382 | |
|
383 | 0 | if (dest || gateway || oif) |
384 | 0 | { |
385 | | /* Unless we're processing the results of a dump, ignore |
386 | | * IPv6 link-local multicast routes, which are added and |
387 | | * removed all the time for some reason. |
388 | | */ |
389 | 0 | #define UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL(a) \ |
390 | 0 | ((a[0] == 0xff) && ((a[1] & 0xf) == 0x2)) |
391 | |
|
392 | 0 | if (!nl->priv->dump_networks && |
393 | 0 | rtmsg->rtm_family == AF_INET6 && |
394 | 0 | rtmsg->rtm_dst_len != 0 && |
395 | 0 | (dest && UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL (dest))) |
396 | 0 | continue; |
397 | | |
398 | 0 | if (msg->nlmsg_type == RTM_NEWROUTE) |
399 | 0 | add_network (nl, rtmsg->rtm_family, dest, rtmsg->rtm_dst_len); |
400 | 0 | else |
401 | 0 | remove_network (nl, rtmsg->rtm_family, dest, rtmsg->rtm_dst_len); |
402 | 0 | queue_request_dump (nl); |
403 | 0 | } |
404 | 0 | break; |
405 | | |
406 | 0 | case NLMSG_DONE: |
407 | 0 | finish_dump (nl); |
408 | 0 | goto done; |
409 | | |
410 | 0 | case NLMSG_ERROR: |
411 | 0 | { |
412 | 0 | struct nlmsgerr *e = NLMSG_DATA (msg); |
413 | |
|
414 | 0 | g_set_error (&local_error, |
415 | 0 | G_IO_ERROR, |
416 | 0 | g_io_error_from_errno (-e->error), |
417 | 0 | "netlink error: %s", |
418 | 0 | g_strerror (-e->error)); |
419 | 0 | } |
420 | 0 | retval = FALSE; |
421 | 0 | goto done; |
422 | | |
423 | 0 | default: |
424 | 0 | g_set_error (&local_error, |
425 | 0 | G_IO_ERROR, |
426 | 0 | G_IO_ERROR_INVALID_DATA, |
427 | 0 | "unexpected netlink message %d", |
428 | 0 | msg->nlmsg_type); |
429 | 0 | retval = FALSE; |
430 | 0 | goto done; |
431 | 0 | } |
432 | 0 | } |
433 | | |
434 | 0 | done: |
435 | 0 | g_free (iv.buffer); |
436 | 0 | g_clear_object (&addr); |
437 | |
|
438 | 0 | if (!retval && nl->priv->dump_networks) |
439 | 0 | finish_dump (nl); |
440 | |
|
441 | 0 | if (local_error) |
442 | 0 | g_propagate_prefixed_error (error, local_error, "Error on netlink socket: "); |
443 | |
|
444 | 0 | return retval; |
445 | 0 | } |
446 | | |
447 | | static void |
448 | | g_network_monitor_netlink_finalize (GObject *object) |
449 | 0 | { |
450 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object); |
451 | |
|
452 | 0 | if (nl->priv->source) |
453 | 0 | { |
454 | 0 | g_source_destroy (nl->priv->source); |
455 | 0 | g_source_unref (nl->priv->source); |
456 | 0 | } |
457 | |
|
458 | 0 | if (nl->priv->dump_source) |
459 | 0 | { |
460 | 0 | g_source_destroy (nl->priv->dump_source); |
461 | 0 | g_source_unref (nl->priv->dump_source); |
462 | 0 | } |
463 | |
|
464 | 0 | if (nl->priv->sock) |
465 | 0 | { |
466 | 0 | g_socket_close (nl->priv->sock, NULL); |
467 | 0 | g_object_unref (nl->priv->sock); |
468 | 0 | } |
469 | |
|
470 | 0 | g_clear_pointer (&nl->priv->context, g_main_context_unref); |
471 | 0 | g_clear_pointer (&nl->priv->dump_networks, g_ptr_array_unref); |
472 | |
|
473 | 0 | G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object); |
474 | 0 | } |
475 | | |
476 | | static gboolean |
477 | | read_netlink_messages_callback (GSocket *socket, |
478 | | GIOCondition condition, |
479 | | gpointer user_data) |
480 | 0 | { |
481 | 0 | GError *error = NULL; |
482 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (user_data); |
483 | |
|
484 | 0 | if (!read_netlink_messages (nl, &error)) |
485 | 0 | { |
486 | 0 | g_warning ("Error reading netlink message: %s", error->message); |
487 | 0 | g_clear_error (&error); |
488 | 0 | return FALSE; |
489 | 0 | } |
490 | | |
491 | 0 | return TRUE; |
492 | 0 | } |
493 | | |
494 | | static void |
495 | | g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class) |
496 | 0 | { |
497 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); |
498 | |
|
499 | 0 | gobject_class->finalize = g_network_monitor_netlink_finalize; |
500 | 0 | } |
501 | | |
502 | | static void |
503 | | g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface) |
504 | 0 | { |
505 | 0 | } |
506 | | |
507 | | static void |
508 | | g_network_monitor_netlink_initable_iface_init (GInitableIface *iface) |
509 | 0 | { |
510 | 0 | initable_parent_iface = g_type_interface_peek_parent (iface); |
511 | |
|
512 | 0 | iface->init = g_network_monitor_netlink_initable_init; |
513 | 0 | } |