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