/src/glib/gio/gnetworkmonitornetlink.c
Line | Count | Source |
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 | | #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) |
27 | | #include <sys/sysctl.h> |
28 | | #endif |
29 | | |
30 | | #include "gnetworkmonitornetlink.h" |
31 | | #include "gcredentials.h" |
32 | | #include "ginetaddress.h" |
33 | | #include "ginetaddressmask.h" |
34 | | #include "ginetsocketaddress.h" |
35 | | #include "ginitable.h" |
36 | | #include "giomodule-priv.h" |
37 | | #include "gioerror.h" |
38 | | #include "glibintl.h" |
39 | | #include "glib/gstdio.h" |
40 | | #include "gnetworkingprivate.h" |
41 | | #include "gnetworkmonitor.h" |
42 | | #include "gsocket.h" |
43 | | #include "gsocketaddressenumerator.h" |
44 | | #include "gsocketconnectable.h" |
45 | | #include "gtask.h" |
46 | | #include "gunixcredentialsmessage.h" |
47 | | |
48 | | /* must come at the end to pick system includes from |
49 | | * gnetworkingprivate.h */ |
50 | | #ifdef HAVE_LINUX_NETLINK_H |
51 | | #include <linux/netlink.h> |
52 | | #include <linux/rtnetlink.h> |
53 | | /* <linux/netlink.h> defines NETLINK_GET_STRICT_CHK but not its setsockopt |
54 | | * level SOL_NETLINK. */ |
55 | | #ifndef SOL_NETLINK |
56 | | #define SOL_NETLINK 270 |
57 | | #endif |
58 | | #endif |
59 | | #if defined(HAVE_NETLINK_NETLINK_H) && defined(HAVE_NETLINK_NETLINK_ROUTE_H) |
60 | | #include <netlink/netlink.h> |
61 | | #include <netlink/netlink_route.h> |
62 | | #endif |
63 | | |
64 | | /* GNetworkMonitorNetlink is the default GNetworkMonitor on Linux when neither |
65 | | * NetworkManager nor systemd-networkd is in charge. |
66 | | * |
67 | | * The kernel routing table (FIB) can be enormous on routers and network-facing |
68 | | * servers (millions of routes), so this backend must never mirror or re-dump the |
69 | | * whole table. It deliberately ignores non-default routes: |
70 | | * |
71 | | * - network-available is derived only from default routes. A single streaming |
72 | | * dump at construction records just the default routes (every route with |
73 | | * rtm_dst_len != 0 is skipped), and the set is then maintained incrementally |
74 | | * from RTM_NEWROUTE/RTM_DELROUTE deltas. The table is re-dumped only to resync |
75 | | * after an ENOBUFS overflow. |
76 | | * |
77 | | * - can_reach() uses an on-demand per-destination RTM_GETROUTE lookup (the |
78 | | * "ip route get" primitive), which is O(prefix length) and independent of the |
79 | | * table size. |
80 | | * |
81 | | * If you change this code, keep it that way: never enumerate or cache the full |
82 | | * FIB. */ |
83 | | |
84 | | static GInitableIface *initable_parent_iface; |
85 | | static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface); |
86 | | static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface); |
87 | | |
88 | | struct _GNetworkMonitorNetlinkPrivate |
89 | | { |
90 | | GSocket *query_sock; /* (mutex query_lock) */ |
91 | | guint query_seq; /* (mutex query_lock) */ |
92 | | GMutex query_lock; |
93 | | |
94 | | GMainContext *context; |
95 | | |
96 | | /* Availability tracking from the kernel routing table. These members are set |
97 | | * up only by setup_monitor(), which runs solely for the plain |
98 | | * GNetworkMonitorNetlink type. Subclasses (NetworkManager, systemd) source |
99 | | * availability elsewhere (e.g. D-Bus) and inherit only can_reach(), so for |
100 | | * them these stay NULL/empty: any code touching them must handle that. */ |
101 | | GSocket *monitor_sock; /* (nullable) */ |
102 | | GSource *source; /* (nullable) */ |
103 | | GSource *dump_source; /* (nullable) */ |
104 | | gboolean dumping; |
105 | | GHashTable *ipv4_defaults; /* (element-type utf8) (owned) (nullable) */ |
106 | | GHashTable *ipv6_defaults; /* (element-type utf8) (owned) (nullable) */ |
107 | | GHashTable *dump_ipv4_defaults; /* (element-type utf8) (owned) (nullable) */ |
108 | | GHashTable *dump_ipv6_defaults; /* (element-type utf8) (owned) (nullable) */ |
109 | | GInetAddressMask *ipv4_default_mask; /* (nullable) */ |
110 | | GInetAddressMask *ipv6_default_mask; /* (nullable) */ |
111 | | }; |
112 | | |
113 | | static gboolean read_netlink_messages (GNetworkMonitorNetlink *nl, |
114 | | GError **error); |
115 | | static gboolean read_netlink_messages_callback (GSocket *socket, |
116 | | GIOCondition condition, |
117 | | gpointer user_data); |
118 | | static gboolean request_dump (GNetworkMonitorNetlink *nl, |
119 | | GError **error); |
120 | | |
121 | | #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type |
122 | 0 | G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE, |
123 | 0 | G_ADD_PRIVATE (GNetworkMonitorNetlink) |
124 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR, |
125 | 0 | g_network_monitor_netlink_iface_init) |
126 | 0 | G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, |
127 | 0 | g_network_monitor_netlink_initable_iface_init) |
128 | 0 | _g_io_modules_ensure_extension_points_registered (); |
129 | 0 | g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME, |
130 | 0 | g_define_type_id, |
131 | 0 | "netlink", |
132 | 0 | 20)) |
133 | 0 |
|
134 | 0 | static void |
135 | 0 | g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl) |
136 | 0 | { |
137 | 0 | nl->priv = g_network_monitor_netlink_get_instance_private (nl); |
138 | 0 | } |
139 | | |
140 | | /* Create a NETLINK_ROUTE socket bound to the given multicast groups (0 for a |
141 | | * request/response socket that is not interested in unsolicited events). */ |
142 | | static GSocket * |
143 | | create_netlink_socket (guint32 groups, |
144 | | GError **error) |
145 | 0 | { |
146 | 0 | gint sockfd; |
147 | 0 | struct sockaddr_nl snl; |
148 | 0 | GSocket *sock; |
149 | 0 | const int rcvbuf = 1024 * 1024; |
150 | | |
151 | | /* We create the socket the old-school way because sockaddr_netlink |
152 | | * can't be represented as a GSocketAddress |
153 | | */ |
154 | 0 | sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL); |
155 | 0 | if (sockfd == -1) |
156 | 0 | { |
157 | 0 | int errsv = errno; |
158 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
159 | 0 | _("Could not create network monitor: %s"), |
160 | 0 | g_strerror (errsv)); |
161 | 0 | return NULL; |
162 | 0 | } |
163 | | |
164 | | /* A host running dynamic routing protocols such as BGP can generate many |
165 | | * route events in a short time; a larger buffer reduces ENOBUFS overflows |
166 | | * that force running the full route enumeration again. */ |
167 | 0 | if (setsockopt (sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof (rcvbuf)) != 0) |
168 | 0 | g_debug ("Could not enlarge netlink receive buffer: %s", g_strerror (errno)); |
169 | |
|
170 | 0 | snl.nl_family = AF_NETLINK; |
171 | 0 | snl.nl_pid = snl.nl_pad = 0; |
172 | 0 | snl.nl_groups = groups; |
173 | 0 | if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0) |
174 | 0 | { |
175 | 0 | int errsv = errno; |
176 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
177 | 0 | _("Could not create network monitor: %s"), |
178 | 0 | g_strerror (errsv)); |
179 | 0 | (void) g_close (sockfd, NULL); |
180 | 0 | return NULL; |
181 | 0 | } |
182 | | |
183 | 0 | sock = g_socket_new_from_fd (sockfd, error); |
184 | 0 | if (!sock) |
185 | 0 | { |
186 | 0 | g_prefix_error (error, "%s", _("Could not create network monitor: ")); |
187 | 0 | (void) g_close (sockfd, NULL); |
188 | 0 | return NULL; |
189 | 0 | } |
190 | | |
191 | 0 | return sock; |
192 | 0 | } |
193 | | |
194 | | static gboolean |
195 | | setup_monitor (GNetworkMonitorNetlink *nl, |
196 | | GError **error) |
197 | 0 | { |
198 | 0 | nl->priv->monitor_sock = create_netlink_socket (RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE, |
199 | 0 | error); |
200 | 0 | if (!nl->priv->monitor_sock) |
201 | 0 | return FALSE; |
202 | | |
203 | 0 | #ifdef SO_PASSCRED |
204 | 0 | if (!g_socket_set_option (nl->priv->monitor_sock, SOL_SOCKET, SO_PASSCRED, |
205 | 0 | TRUE, NULL)) |
206 | 0 | { |
207 | 0 | int errsv = errno; |
208 | 0 | g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), |
209 | 0 | _("Could not create network monitor: %s"), |
210 | 0 | g_strerror (errsv)); |
211 | 0 | return FALSE; |
212 | 0 | } |
213 | 0 | #endif |
214 | | |
215 | 0 | nl->priv->ipv4_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
216 | 0 | nl->priv->ipv6_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
217 | | |
218 | | /* Request the current state to find existing default routes */ |
219 | 0 | if (!request_dump (nl, error)) |
220 | 0 | return FALSE; |
221 | | |
222 | | /* And read responses; since we haven't yet marked the socket |
223 | | * non-blocking, each call will block until a message is received. |
224 | | */ |
225 | 0 | while (nl->priv->dumping) |
226 | 0 | { |
227 | 0 | GError *local_error = NULL; |
228 | 0 | if (!read_netlink_messages (nl, &local_error)) |
229 | 0 | { |
230 | 0 | g_warning ("%s", local_error->message); |
231 | 0 | g_clear_error (&local_error); |
232 | 0 | break; |
233 | 0 | } |
234 | 0 | } |
235 | |
|
236 | 0 | g_socket_set_blocking (nl->priv->monitor_sock, FALSE); |
237 | 0 | nl->priv->source = g_socket_create_source (nl->priv->monitor_sock, G_IO_IN, NULL); |
238 | 0 | g_source_set_callback (nl->priv->source, |
239 | 0 | (GSourceFunc) read_netlink_messages_callback, nl, NULL); |
240 | 0 | g_source_attach (nl->priv->source, nl->priv->context); |
241 | |
|
242 | 0 | return TRUE; |
243 | 0 | } |
244 | | |
245 | | static gboolean |
246 | | g_network_monitor_netlink_initable_init (GInitable *initable, |
247 | | GCancellable *cancellable, |
248 | | GError **error) |
249 | 0 | { |
250 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable); |
251 | |
|
252 | 0 | g_mutex_init (&nl->priv->query_lock); |
253 | 0 | nl->priv->context = g_main_context_ref_thread_default (); |
254 | | |
255 | | /* A socket for on-demand per-destination route lookups, used by can_reach() |
256 | | * for this backend and every subclass. */ |
257 | 0 | nl->priv->query_sock = create_netlink_socket (0, error); |
258 | 0 | if (!nl->priv->query_sock) |
259 | 0 | return FALSE; |
260 | | |
261 | | /* Availability tracking is set up only for the plain netlink type; see the |
262 | | * GNetworkMonitorNetlinkPrivate definition. Subclasses inherit can_reach(). */ |
263 | 0 | if (G_OBJECT_TYPE (nl) == G_TYPE_NETWORK_MONITOR_NETLINK && |
264 | 0 | !setup_monitor (nl, error)) |
265 | 0 | return FALSE; |
266 | | |
267 | 0 | return initable_parent_iface->init (initable, cancellable, error); |
268 | 0 | } |
269 | | |
270 | | static gboolean |
271 | | request_dump (GNetworkMonitorNetlink *nl, |
272 | | GError **error) |
273 | 0 | { |
274 | 0 | struct nlmsghdr *n; |
275 | 0 | struct rtmsg *rtm; |
276 | 0 | gchar buf[NLMSG_SPACE (sizeof (*rtm))]; |
277 | |
|
278 | 0 | #ifdef NETLINK_GET_STRICT_CHK |
279 | | /* Strict checking with rtm_flags == 0 lets the kernel dump faster by |
280 | | * skipping unnecessary data, e.g. the route-cache exceptions. */ |
281 | 0 | { |
282 | 0 | const int strict = 1; |
283 | 0 | if (setsockopt (g_socket_get_fd (nl->priv->monitor_sock), SOL_NETLINK, |
284 | 0 | NETLINK_GET_STRICT_CHK, &strict, sizeof (strict)) != 0) |
285 | 0 | g_debug ("Could not set NETLINK_GET_STRICT_CHK: %s", g_strerror (errno)); |
286 | 0 | } |
287 | 0 | #endif |
288 | |
|
289 | 0 | memset (buf, 0, sizeof (buf)); |
290 | 0 | n = (struct nlmsghdr*) buf; |
291 | 0 | n->nlmsg_len = NLMSG_LENGTH (sizeof (*rtm)); |
292 | 0 | n->nlmsg_type = RTM_GETROUTE; |
293 | 0 | n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
294 | 0 | n->nlmsg_pid = 0; |
295 | 0 | rtm = NLMSG_DATA (n); |
296 | 0 | rtm->rtm_family = AF_UNSPEC; |
297 | | /* Keep this request minimal: a strict dump requires the selector fields to |
298 | | * stay zero, and extra attributes can also hurt dump performance. */ |
299 | |
|
300 | 0 | if (g_socket_send (nl->priv->monitor_sock, buf, sizeof (buf), |
301 | 0 | NULL, error) < 0) |
302 | 0 | { |
303 | 0 | g_prefix_error (error, "%s", _("Could not get network status: ")); |
304 | 0 | return FALSE; |
305 | 0 | } |
306 | | |
307 | 0 | nl->priv->dumping = TRUE; |
308 | 0 | nl->priv->dump_ipv4_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
309 | 0 | nl->priv->dump_ipv6_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
310 | 0 | return TRUE; |
311 | 0 | } |
312 | | |
313 | | static gboolean |
314 | | timeout_request_dump (gpointer user_data) |
315 | 0 | { |
316 | 0 | GNetworkMonitorNetlink *nl = user_data; |
317 | |
|
318 | 0 | g_source_destroy (nl->priv->dump_source); |
319 | 0 | g_source_unref (nl->priv->dump_source); |
320 | 0 | nl->priv->dump_source = NULL; |
321 | |
|
322 | 0 | request_dump (nl, NULL); |
323 | |
|
324 | 0 | return FALSE; |
325 | 0 | } |
326 | | |
327 | | /* Schedule a full re-dump to rebuild default-route state after the monitor |
328 | | * socket overflows (ENOBUFS). Normal default-route changes are tracked |
329 | | * incrementally and do not trigger a dump. */ |
330 | | static void |
331 | | queue_request_dump (GNetworkMonitorNetlink *nl) |
332 | 0 | { |
333 | 0 | if (nl->priv->dumping) |
334 | 0 | return; |
335 | | |
336 | 0 | if (nl->priv->dump_source) |
337 | 0 | { |
338 | 0 | g_source_destroy (nl->priv->dump_source); |
339 | 0 | g_source_unref (nl->priv->dump_source); |
340 | 0 | } |
341 | |
|
342 | 0 | nl->priv->dump_source = g_timeout_source_new_seconds (1); |
343 | 0 | g_source_set_callback (nl->priv->dump_source, |
344 | 0 | (GSourceFunc) timeout_request_dump, nl, NULL); |
345 | 0 | g_source_attach (nl->priv->dump_source, nl->priv->context); |
346 | 0 | } |
347 | | |
348 | | static GInetAddressMask * |
349 | | default_route_mask (GNetworkMonitorNetlink *nl, |
350 | | int family) |
351 | 0 | { |
352 | 0 | GInetAddressMask **maskp; |
353 | 0 | GSocketFamily socket_family; |
354 | |
|
355 | 0 | if (family == AF_INET) |
356 | 0 | { |
357 | 0 | maskp = &nl->priv->ipv4_default_mask; |
358 | 0 | socket_family = G_SOCKET_FAMILY_IPV4; |
359 | 0 | } |
360 | 0 | else if (family == AF_INET6) |
361 | 0 | { |
362 | 0 | maskp = &nl->priv->ipv6_default_mask; |
363 | 0 | socket_family = G_SOCKET_FAMILY_IPV6; |
364 | 0 | } |
365 | 0 | else |
366 | 0 | { |
367 | 0 | g_assert_not_reached (); |
368 | 0 | } |
369 | | |
370 | 0 | if (*maskp == NULL) |
371 | 0 | { |
372 | 0 | GInetAddress *any = g_inet_address_new_any (socket_family); |
373 | 0 | *maskp = g_inet_address_mask_new (any, 0, NULL); |
374 | 0 | g_object_unref (any); |
375 | 0 | } |
376 | |
|
377 | 0 | return *maskp; |
378 | 0 | } |
379 | | |
380 | | /* Reflect presence of a default route for @family into the base monitor's |
381 | | * network set, which drives the network-available property. */ |
382 | | static void |
383 | | update_default_route_available (GNetworkMonitorNetlink *nl, |
384 | | int family, |
385 | | gboolean available) |
386 | 0 | { |
387 | 0 | GInetAddressMask *mask = default_route_mask (nl, family); |
388 | |
|
389 | 0 | if (mask == NULL) |
390 | 0 | return; |
391 | | |
392 | 0 | if (available) |
393 | 0 | g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), mask); |
394 | 0 | else |
395 | 0 | g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), mask); |
396 | 0 | } |
397 | | |
398 | | static void |
399 | | add_default_route (GNetworkMonitorNetlink *nl, |
400 | | int family, |
401 | | gchar *stolen_key /* (transfer full) */) |
402 | 0 | { |
403 | 0 | GHashTable *set; |
404 | |
|
405 | 0 | if (nl->priv->dumping) |
406 | 0 | set = (family == AF_INET) ? nl->priv->dump_ipv4_defaults : nl->priv->dump_ipv6_defaults; |
407 | 0 | else |
408 | 0 | set = (family == AF_INET) ? nl->priv->ipv4_defaults : nl->priv->ipv6_defaults; |
409 | |
|
410 | 0 | if (!nl->priv->dumping && g_hash_table_size (set) == 0) |
411 | 0 | update_default_route_available (nl, family, TRUE); |
412 | |
|
413 | 0 | g_hash_table_add (set, g_steal_pointer (&stolen_key)); |
414 | 0 | } |
415 | | |
416 | | static void |
417 | | remove_default_route (GNetworkMonitorNetlink *nl, |
418 | | int family, |
419 | | const gchar *key) |
420 | 0 | { |
421 | 0 | GHashTable *set; |
422 | | |
423 | | /* An RTM_DELROUTE is always a multicast event and can interleave with a |
424 | | * (re-)dump, so this may run while dumping; update the live set, which |
425 | | * finish_dump() then reconciles against the freshly dumped set. */ |
426 | 0 | set = (family == AF_INET) ? nl->priv->ipv4_defaults : nl->priv->ipv6_defaults; |
427 | |
|
428 | 0 | if (g_hash_table_remove (set, key) && |
429 | 0 | g_hash_table_size (set) == 0) |
430 | 0 | update_default_route_available (nl, family, FALSE); |
431 | 0 | } |
432 | | |
433 | | static void |
434 | | finish_dump (GNetworkMonitorNetlink *nl) |
435 | 0 | { |
436 | 0 | int families[2] = { AF_INET, AF_INET6 }; |
437 | 0 | size_t i; |
438 | | |
439 | | /* Reconcile the freshly-dumped default routes with the live state, emitting |
440 | | * an availability change only when the per-family presence actually flips. */ |
441 | 0 | for (i = 0; i < G_N_ELEMENTS (families); i++) |
442 | 0 | { |
443 | 0 | int family = families[i]; |
444 | 0 | GHashTable **livep = (family == AF_INET) ? &nl->priv->ipv4_defaults : &nl->priv->ipv6_defaults; |
445 | 0 | GHashTable **dumpp = (family == AF_INET) ? &nl->priv->dump_ipv4_defaults : &nl->priv->dump_ipv6_defaults; |
446 | 0 | gboolean was_available = g_hash_table_size (*livep) > 0; |
447 | 0 | gboolean now_available = g_hash_table_size (*dumpp) > 0; |
448 | |
|
449 | 0 | g_hash_table_unref (*livep); |
450 | 0 | *livep = g_steal_pointer (dumpp); |
451 | |
|
452 | 0 | if (was_available != now_available) |
453 | 0 | update_default_route_available (nl, family, now_available); |
454 | 0 | } |
455 | |
|
456 | 0 | nl->priv->dumping = FALSE; |
457 | | |
458 | | /* FreeBSD features "jailing" functionality, which can be approximated to |
459 | | * Linux namespaces. A jail may or may not share the host's network stack, |
460 | | * which includes routing tables. |
461 | | * When jail runs in non-vnet mode and has a shared stack with the host, |
462 | | * the kernel prevents jailed processes from getting full view on a routing |
463 | | * table. This makes GNetworkManager believe that we're offline and return |
464 | | * FALSE for the "available" property. |
465 | | * To workaround this problem, do the same thing as GNetworkMonitorBase - |
466 | | * add a fake network of 0 length. |
467 | | */ |
468 | | #ifdef __FreeBSD__ |
469 | | gboolean is_jailed = FALSE; |
470 | | gsize len = sizeof (is_jailed); |
471 | | |
472 | | if (sysctlbyname ("security.jail.jailed", &is_jailed, &len, NULL, 0) != 0) |
473 | | return; |
474 | | |
475 | | if (!is_jailed) |
476 | | return; |
477 | | |
478 | | if (is_jailed && !g_network_monitor_get_network_available (G_NETWORK_MONITOR (nl))) |
479 | | { |
480 | | GInetAddressMask *network; |
481 | | network = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL); |
482 | | g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network); |
483 | | g_object_unref (network); |
484 | | } |
485 | | #endif |
486 | 0 | } |
487 | | |
488 | | /* A route whose type does not actually deliver packets to the destination |
489 | | * (unreachable, blackhole, prohibit, throw) does not count as reachable. */ |
490 | | static gboolean |
491 | | route_type_is_reachable (unsigned char rtm_type) |
492 | 0 | { |
493 | 0 | return (rtm_type != RTN_UNREACHABLE && |
494 | 0 | rtm_type != RTN_BLACKHOLE && |
495 | 0 | rtm_type != RTN_PROHIBIT && |
496 | 0 | rtm_type != RTN_THROW); |
497 | 0 | } |
498 | | |
499 | | /* Sane upper bound on a single netlink datagram before g_malloc(): a route-get |
500 | | * reply is one message (<= NLMSG_GOODSIZE, < 8 KiB), while a dump datagram packs |
501 | | * many messages and is hard-capped by the kernel at SKB_WITH_OVERHEAD(32768) |
502 | | * (~32 KiB, see netlink_recvmsg()). 64 KiB leaves headroom over both. */ |
503 | 0 | #define NETLINK_MESSAGE_MAX_SIZE (64 * 1024) |
504 | | |
505 | | static gboolean |
506 | | read_netlink_messages (GNetworkMonitorNetlink *nl, |
507 | | GError **error) |
508 | 0 | { |
509 | 0 | GInputVector iv; |
510 | 0 | gssize len; |
511 | 0 | gint flags; |
512 | 0 | GError *local_error = NULL; |
513 | 0 | GSocketAddress *addr = NULL; |
514 | 0 | struct nlmsghdr *msg; |
515 | 0 | struct rtmsg *rtmsg; |
516 | 0 | struct rtattr *attr; |
517 | 0 | struct sockaddr_nl source_sockaddr; |
518 | 0 | gsize attrlen; |
519 | 0 | guint32 table, metric, oif; |
520 | 0 | gboolean have_nexthop; |
521 | | |
522 | | /* The kernel is aware of the read buffer size, so a larger buffer lets it |
523 | | * deliver more data per datagram. */ |
524 | 0 | iv.buffer = g_malloc (NETLINK_MESSAGE_MAX_SIZE); |
525 | 0 | iv.size = NETLINK_MESSAGE_MAX_SIZE; |
526 | 0 | flags = 0; |
527 | 0 | len = g_socket_receive_message (nl->priv->monitor_sock, &addr, &iv, 1, |
528 | 0 | NULL, NULL, &flags, NULL, &local_error); |
529 | 0 | if (len < 0) |
530 | 0 | goto done; |
531 | 0 | if (len == 0 || (flags & MSG_TRUNC)) |
532 | 0 | { |
533 | 0 | g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, |
534 | 0 | "netlink message length is invalid"); |
535 | 0 | goto done; |
536 | 0 | } |
537 | | |
538 | 0 | if (!g_socket_address_to_native (addr, &source_sockaddr, sizeof (source_sockaddr), &local_error)) |
539 | 0 | goto done; |
540 | | |
541 | | /* If the sender port id is 0 (not fakeable) then the message is from the kernel */ |
542 | 0 | if (source_sockaddr.nl_pid != 0) |
543 | 0 | goto done; |
544 | | |
545 | 0 | msg = (struct nlmsghdr *) iv.buffer; |
546 | 0 | for (; len > 0; msg = NLMSG_NEXT (msg, len)) |
547 | 0 | { |
548 | 0 | if (!NLMSG_OK (msg, (size_t) len)) |
549 | 0 | { |
550 | 0 | g_set_error_literal (&local_error, |
551 | 0 | G_IO_ERROR, |
552 | 0 | G_IO_ERROR_PARTIAL_INPUT, |
553 | 0 | "netlink message was truncated; shouldn't happen..."); |
554 | 0 | goto done; |
555 | 0 | } |
556 | | |
557 | 0 | switch (msg->nlmsg_type) |
558 | 0 | { |
559 | 0 | case RTM_NEWROUTE: |
560 | 0 | case RTM_DELROUTE: |
561 | 0 | rtmsg = NLMSG_DATA (msg); |
562 | |
|
563 | 0 | if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6) |
564 | 0 | continue; |
565 | | /* Only default routes (0-length prefix) affect network-available. */ |
566 | 0 | if (rtmsg->rtm_dst_len != 0) |
567 | 0 | continue; |
568 | 0 | if (!route_type_is_reachable (rtmsg->rtm_type)) |
569 | 0 | continue; |
570 | | |
571 | 0 | table = rtmsg->rtm_table; |
572 | 0 | metric = 0; |
573 | 0 | oif = 0; |
574 | 0 | have_nexthop = FALSE; |
575 | |
|
576 | 0 | attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg)); |
577 | 0 | attr = RTM_RTA (rtmsg); |
578 | 0 | while (RTA_OK (attr, attrlen)) |
579 | 0 | { |
580 | 0 | if (attr->rta_type == RTA_GATEWAY) |
581 | 0 | have_nexthop = TRUE; |
582 | 0 | else if (attr->rta_type == RTA_OIF) |
583 | 0 | { |
584 | 0 | oif = *(guint32 *) RTA_DATA (attr); |
585 | 0 | have_nexthop = TRUE; |
586 | 0 | } |
587 | 0 | else if (attr->rta_type == RTA_PRIORITY) |
588 | 0 | metric = *(guint32 *) RTA_DATA (attr); |
589 | 0 | else if (attr->rta_type == RTA_TABLE) |
590 | 0 | table = *(guint32 *) RTA_DATA (attr); |
591 | 0 | else if (attr->rta_type == RTA_MULTIPATH) |
592 | 0 | have_nexthop = TRUE; |
593 | 0 | attr = RTA_NEXT (attr, attrlen); |
594 | 0 | } |
595 | | |
596 | | /* A default route reaches a nexthop: a gateway, an output interface, |
597 | | * or a set of multipath nexthops. */ |
598 | 0 | if (have_nexthop) |
599 | 0 | { |
600 | | /* Key by attributes that survive "ip route replace" (which emits |
601 | | * RTM_NEWROUTE with no matching RTM_DELROUTE), so the set tracks |
602 | | * distinct default routes without drifting. */ |
603 | 0 | gchar *key = g_strdup_printf ("%u:%u:%u", table, metric, oif); |
604 | |
|
605 | 0 | if (msg->nlmsg_type == RTM_NEWROUTE) |
606 | 0 | add_default_route (nl, rtmsg->rtm_family, g_steal_pointer (&key)); |
607 | 0 | else |
608 | 0 | remove_default_route (nl, rtmsg->rtm_family, key); |
609 | 0 | g_free (key); |
610 | 0 | } |
611 | 0 | break; |
612 | | |
613 | 0 | case NLMSG_DONE: |
614 | 0 | finish_dump (nl); |
615 | 0 | goto done; |
616 | | |
617 | 0 | case NLMSG_ERROR: |
618 | 0 | { |
619 | 0 | struct nlmsgerr *e = NLMSG_DATA (msg); |
620 | | |
621 | | /* ENOBUFS means the socket buffer overflowed and we may have missed |
622 | | * default-route changes; resync by re-dumping. */ |
623 | 0 | if (e->error == -ENOBUFS) |
624 | 0 | { |
625 | 0 | queue_request_dump (nl); |
626 | 0 | goto done; |
627 | 0 | } |
628 | | |
629 | 0 | g_set_error (&local_error, |
630 | 0 | G_IO_ERROR, |
631 | 0 | g_io_error_from_errno (-e->error), |
632 | 0 | "netlink error: %s", |
633 | 0 | g_strerror (-e->error)); |
634 | 0 | } |
635 | 0 | goto done; |
636 | | |
637 | 0 | default: |
638 | 0 | g_set_error (&local_error, |
639 | 0 | G_IO_ERROR, |
640 | 0 | G_IO_ERROR_INVALID_DATA, |
641 | 0 | "unexpected netlink message %d", |
642 | 0 | msg->nlmsg_type); |
643 | 0 | goto done; |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | 0 | done: |
648 | 0 | g_free (iv.buffer); |
649 | 0 | g_clear_object (&addr); |
650 | |
|
651 | 0 | if (local_error != NULL && nl->priv->dumping) |
652 | 0 | finish_dump (nl); |
653 | |
|
654 | 0 | if (local_error != NULL) |
655 | 0 | { |
656 | 0 | g_propagate_prefixed_error (error, local_error, "Error on netlink socket: "); |
657 | 0 | return FALSE; |
658 | 0 | } |
659 | 0 | else |
660 | 0 | { |
661 | 0 | return TRUE; |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | | static gboolean |
666 | | read_netlink_messages_callback (GSocket *socket, |
667 | | GIOCondition condition, |
668 | | gpointer user_data) |
669 | 0 | { |
670 | 0 | GError *error = NULL; |
671 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (user_data); |
672 | |
|
673 | 0 | if (!read_netlink_messages (nl, &error)) |
674 | 0 | { |
675 | 0 | g_warning ("Error reading netlink message: %s", error->message); |
676 | 0 | g_clear_error (&error); |
677 | 0 | return FALSE; |
678 | 0 | } |
679 | | |
680 | 0 | return TRUE; |
681 | 0 | } |
682 | | |
683 | | /* Map a GSocketFamily to its AF_* address family constant. */ |
684 | | static int |
685 | | socket_family_to_af_family (GSocketFamily socket_family) |
686 | 0 | { |
687 | 0 | switch (socket_family) |
688 | 0 | { |
689 | 0 | case G_SOCKET_FAMILY_IPV4: |
690 | 0 | return AF_INET; |
691 | 0 | case G_SOCKET_FAMILY_IPV6: |
692 | 0 | return AF_INET6; |
693 | 0 | default: |
694 | 0 | g_assert_not_reached (); |
695 | 0 | } |
696 | 0 | } |
697 | | |
698 | | /* Map a kernel route-lookup errno to the GIOError reported by can_reach(). */ |
699 | | static GIOErrorEnum |
700 | | route_get_error_from_errno (int err) |
701 | 0 | { |
702 | 0 | switch (err) |
703 | 0 | { |
704 | 0 | case ENETUNREACH: |
705 | 0 | case ENETDOWN: |
706 | 0 | return G_IO_ERROR_NETWORK_UNREACHABLE; |
707 | 0 | case EHOSTUNREACH: |
708 | 0 | case EHOSTDOWN: |
709 | 0 | case EACCES: |
710 | 0 | return G_IO_ERROR_HOST_UNREACHABLE; |
711 | 0 | default: |
712 | 0 | return g_io_error_from_errno (err); |
713 | 0 | } |
714 | 0 | } |
715 | | |
716 | | /* Perform a single per-destination route lookup ("ip route get"). Returns TRUE |
717 | | * if @addr is reachable, otherwise FALSE with @error set. The error is usually |
718 | | * G_IO_ERROR_HOST_UNREACHABLE or G_IO_ERROR_NETWORK_UNREACHABLE, but any other |
719 | | * GIOError that route_get_error_from_errno() maps from the kernel's errno is |
720 | | * possible. Blocking, and safe to call from a worker thread. */ |
721 | | static gboolean |
722 | | route_get (GNetworkMonitorNetlink *nl, |
723 | | GInetAddress *addr, |
724 | | GCancellable *cancellable, |
725 | | GError **error) |
726 | 0 | { |
727 | 0 | GSocketFamily socket_family = g_inet_address_get_family (addr); |
728 | 0 | int family = socket_family_to_af_family (socket_family); |
729 | 0 | gsize addrlen = g_inet_address_get_native_size (addr); |
730 | 0 | const guint8 *addrbytes = g_inet_address_to_bytes (addr); |
731 | 0 | struct { |
732 | 0 | struct nlmsghdr n; |
733 | 0 | struct rtmsg r; |
734 | 0 | gchar buf[64]; |
735 | 0 | } req; |
736 | 0 | struct nlmsghdr *msg; |
737 | 0 | struct rtmsg *rtmsg; |
738 | 0 | struct rtattr *rta; |
739 | 0 | GInputVector iv = { NULL, 0 }; |
740 | 0 | struct sockaddr_nl source_sockaddr; |
741 | 0 | GSocketAddress *src_addr = NULL; |
742 | 0 | GError *local_error = NULL; |
743 | 0 | gssize len; |
744 | 0 | gint flags; |
745 | 0 | guint seq; |
746 | 0 | gboolean reachable = FALSE; |
747 | 0 | gboolean got_reply = FALSE; |
748 | |
|
749 | 0 | memset (&req, 0, sizeof (req)); |
750 | 0 | req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); |
751 | 0 | req.n.nlmsg_type = RTM_GETROUTE; |
752 | 0 | req.n.nlmsg_flags = NLM_F_REQUEST; |
753 | 0 | req.r.rtm_family = family; |
754 | 0 | req.r.rtm_dst_len = addrlen * 8; /* this field is a CIDR length in bits */ |
755 | | |
756 | | /* We want the kernel to resolve the route to @addr. RTM_F_FIB_MATCH |
757 | | * (Linux 4.11+) makes it return the matching FIB entry; without the flag the |
758 | | * kernel returns a per-destination cloned route instead, which still works |
759 | | * as we are currently interested in a route existence. */ |
760 | 0 | #ifdef RTM_F_FIB_MATCH |
761 | 0 | req.r.rtm_flags = RTM_F_FIB_MATCH; |
762 | 0 | #endif |
763 | |
|
764 | 0 | rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN (req.n.nlmsg_len)); |
765 | 0 | rta->rta_type = RTA_DST; |
766 | 0 | rta->rta_len = RTA_LENGTH (addrlen); |
767 | 0 | g_assert ((char *) rta + rta->rta_len <= (char *) &req + sizeof (req)); |
768 | 0 | memcpy (RTA_DATA (rta), addrbytes, addrlen); |
769 | 0 | req.n.nlmsg_len = NLMSG_ALIGN (req.n.nlmsg_len) + rta->rta_len; |
770 | |
|
771 | 0 | g_mutex_lock (&nl->priv->query_lock); |
772 | |
|
773 | 0 | seq = ++nl->priv->query_seq; |
774 | 0 | req.n.nlmsg_seq = seq; |
775 | |
|
776 | 0 | if (g_socket_send (nl->priv->query_sock, (const gchar *) &req, req.n.nlmsg_len, |
777 | 0 | cancellable, &local_error) < 0) |
778 | 0 | goto done; |
779 | | |
780 | 0 | flags = MSG_PEEK | MSG_TRUNC; |
781 | 0 | len = g_socket_receive_message (nl->priv->query_sock, NULL, &iv, 1, |
782 | 0 | NULL, NULL, &flags, cancellable, &local_error); |
783 | 0 | if (len < 0) |
784 | 0 | goto done; |
785 | 0 | if (len == 0 || len > NETLINK_MESSAGE_MAX_SIZE) |
786 | 0 | { |
787 | 0 | g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, |
788 | 0 | "netlink route reply length is invalid"); |
789 | 0 | goto done; |
790 | 0 | } |
791 | | |
792 | 0 | iv.buffer = g_malloc (len); |
793 | 0 | iv.size = len; |
794 | 0 | len = g_socket_receive_message (nl->priv->query_sock, &src_addr, &iv, 1, |
795 | 0 | NULL, NULL, NULL, cancellable, &local_error); |
796 | 0 | if (len < 0) |
797 | 0 | goto done; |
798 | | |
799 | | /* Only trust messages from the kernel (sender port id 0). */ |
800 | 0 | if (!g_socket_address_to_native (src_addr, &source_sockaddr, sizeof (source_sockaddr), &local_error)) |
801 | 0 | goto done; |
802 | 0 | if (source_sockaddr.nl_pid != 0) |
803 | 0 | goto done; |
804 | | |
805 | 0 | msg = (struct nlmsghdr *) iv.buffer; |
806 | 0 | for (; len > 0; msg = NLMSG_NEXT (msg, len)) |
807 | 0 | { |
808 | 0 | if (!NLMSG_OK (msg, (size_t) len)) |
809 | 0 | break; |
810 | 0 | if (msg->nlmsg_seq != seq) |
811 | 0 | continue; |
812 | | |
813 | 0 | if (msg->nlmsg_type == NLMSG_ERROR) |
814 | 0 | { |
815 | 0 | struct nlmsgerr *e = NLMSG_DATA (msg); |
816 | |
|
817 | 0 | got_reply = TRUE; |
818 | | /* error == 0 is a success acknowledgement, which the kernel never sends for our request. */ |
819 | 0 | if (e->error != 0) |
820 | 0 | g_set_error_literal (&local_error, G_IO_ERROR, |
821 | 0 | route_get_error_from_errno (-e->error), |
822 | 0 | g_strerror (-e->error)); |
823 | 0 | break; |
824 | 0 | } |
825 | 0 | else if (msg->nlmsg_type == RTM_NEWROUTE) |
826 | 0 | { |
827 | 0 | rtmsg = NLMSG_DATA (msg); |
828 | 0 | got_reply = TRUE; |
829 | 0 | reachable = route_type_is_reachable (rtmsg->rtm_type); |
830 | 0 | if (!reachable) |
831 | 0 | g_set_error_literal (&local_error, G_IO_ERROR, |
832 | 0 | G_IO_ERROR_HOST_UNREACHABLE, |
833 | 0 | _("Host unreachable")); |
834 | 0 | break; |
835 | 0 | } |
836 | 0 | } |
837 | |
|
838 | 0 | done: |
839 | 0 | g_mutex_unlock (&nl->priv->query_lock); |
840 | |
|
841 | 0 | g_free (iv.buffer); |
842 | 0 | g_clear_object (&src_addr); |
843 | |
|
844 | 0 | if (reachable) |
845 | 0 | { |
846 | 0 | g_assert (local_error == NULL); |
847 | 0 | return TRUE; |
848 | 0 | } |
849 | | |
850 | 0 | if (local_error != NULL) |
851 | 0 | g_propagate_error (error, g_steal_pointer (&local_error)); |
852 | 0 | else if (!got_reply) |
853 | 0 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE, |
854 | 0 | _("Host unreachable")); |
855 | 0 | return FALSE; |
856 | 0 | } |
857 | | |
858 | | static gboolean |
859 | | g_network_monitor_netlink_can_reach (GNetworkMonitor *monitor, |
860 | | GSocketConnectable *connectable, |
861 | | GCancellable *cancellable, |
862 | | GError **error) |
863 | 0 | { |
864 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (monitor); |
865 | 0 | GSocketAddressEnumerator *enumerator; |
866 | 0 | GSocketAddress *addr; |
867 | 0 | GError *local_error = NULL; |
868 | |
|
869 | 0 | enumerator = g_socket_connectable_proxy_enumerate (connectable); |
870 | | |
871 | | /* Try each resolved address; the host is reachable if any of them is. A |
872 | | * failed route_get() is cleared so the next g_socket_address_enumerator_next() |
873 | | * sees a clean @local_error, except on cancellation, which we propagate. */ |
874 | 0 | while ((addr = g_socket_address_enumerator_next (enumerator, cancellable, &local_error))) |
875 | 0 | { |
876 | 0 | if (G_IS_INET_SOCKET_ADDRESS (addr)) |
877 | 0 | { |
878 | 0 | GInetAddress *iaddr; |
879 | |
|
880 | 0 | iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); |
881 | 0 | if (route_get (nl, iaddr, cancellable, &local_error)) |
882 | 0 | { |
883 | 0 | g_object_unref (addr); |
884 | 0 | g_object_unref (enumerator); |
885 | 0 | return TRUE; |
886 | 0 | } |
887 | | |
888 | 0 | if (g_cancellable_is_cancelled (cancellable)) |
889 | 0 | { |
890 | 0 | g_object_unref (addr); |
891 | 0 | break; |
892 | 0 | } |
893 | 0 | g_clear_error (&local_error); |
894 | 0 | } |
895 | | |
896 | 0 | g_object_unref (addr); |
897 | 0 | } |
898 | 0 | g_object_unref (enumerator); |
899 | | |
900 | | /* An enumeration failure (DNS) or cancellation is reported as-is. */ |
901 | 0 | if (local_error != NULL) |
902 | 0 | { |
903 | 0 | g_propagate_error (error, g_steal_pointer (&local_error)); |
904 | 0 | return FALSE; |
905 | 0 | } |
906 | | |
907 | | /* Reached when no address was routable (each route_get() error was cleared to |
908 | | * try the next one) or none were `GInetSocketAddress`es; either way report a |
909 | | * generic error, matching the base monitor. */ |
910 | 0 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_HOST_UNREACHABLE, |
911 | 0 | _("Host unreachable")); |
912 | 0 | return FALSE; |
913 | 0 | } |
914 | | |
915 | | static void |
916 | | can_reach_thread (GTask *task, |
917 | | gpointer source_object, |
918 | | gpointer task_data, |
919 | | GCancellable *cancellable) |
920 | 0 | { |
921 | 0 | GNetworkMonitor *monitor = G_NETWORK_MONITOR (source_object); |
922 | 0 | GSocketConnectable *connectable = G_SOCKET_CONNECTABLE (task_data); |
923 | 0 | GError *error = NULL; |
924 | |
|
925 | 0 | if (g_network_monitor_netlink_can_reach (monitor, connectable, cancellable, &error)) |
926 | 0 | g_task_return_boolean (task, TRUE); |
927 | 0 | else |
928 | 0 | g_task_return_error (task, g_steal_pointer (&error)); |
929 | 0 | } |
930 | | |
931 | | static void |
932 | | g_network_monitor_netlink_can_reach_async (GNetworkMonitor *monitor, |
933 | | GSocketConnectable *connectable, |
934 | | GCancellable *cancellable, |
935 | | GAsyncReadyCallback callback, |
936 | | gpointer user_data) |
937 | 0 | { |
938 | 0 | GTask *task; |
939 | |
|
940 | 0 | task = g_task_new (monitor, cancellable, callback, user_data); |
941 | 0 | g_task_set_source_tag (task, g_network_monitor_netlink_can_reach_async); |
942 | 0 | g_task_set_static_name (task, "[gio] network monitor can_reach"); |
943 | 0 | g_task_set_task_data (task, g_object_ref (connectable), g_object_unref); |
944 | | |
945 | | /* A route lookup is a quick kernel round-trip, so the shared GTask thread |
946 | | * pool is fine here. */ |
947 | 0 | g_task_run_in_thread (task, can_reach_thread); |
948 | 0 | g_object_unref (task); |
949 | 0 | } |
950 | | |
951 | | static gboolean |
952 | | g_network_monitor_netlink_can_reach_finish (GNetworkMonitor *monitor, |
953 | | GAsyncResult *result, |
954 | | GError **error) |
955 | 0 | { |
956 | 0 | g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE); |
957 | | |
958 | 0 | return g_task_propagate_boolean (G_TASK (result), error); |
959 | 0 | } |
960 | | |
961 | | static void |
962 | | g_network_monitor_netlink_finalize (GObject *object) |
963 | 0 | { |
964 | 0 | GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object); |
965 | |
|
966 | 0 | if (nl->priv->source) |
967 | 0 | { |
968 | 0 | g_source_destroy (nl->priv->source); |
969 | 0 | g_source_unref (nl->priv->source); |
970 | 0 | } |
971 | |
|
972 | 0 | if (nl->priv->dump_source) |
973 | 0 | { |
974 | 0 | g_source_destroy (nl->priv->dump_source); |
975 | 0 | g_source_unref (nl->priv->dump_source); |
976 | 0 | } |
977 | |
|
978 | 0 | if (nl->priv->query_sock) |
979 | 0 | { |
980 | 0 | g_socket_close (nl->priv->query_sock, NULL); |
981 | 0 | g_object_unref (nl->priv->query_sock); |
982 | 0 | } |
983 | |
|
984 | 0 | if (nl->priv->monitor_sock) |
985 | 0 | { |
986 | 0 | g_socket_close (nl->priv->monitor_sock, NULL); |
987 | 0 | g_object_unref (nl->priv->monitor_sock); |
988 | 0 | } |
989 | |
|
990 | 0 | g_mutex_clear (&nl->priv->query_lock); |
991 | |
|
992 | 0 | g_clear_pointer (&nl->priv->context, g_main_context_unref); |
993 | 0 | g_clear_pointer (&nl->priv->ipv4_defaults, g_hash_table_unref); |
994 | 0 | g_clear_pointer (&nl->priv->ipv6_defaults, g_hash_table_unref); |
995 | 0 | g_clear_pointer (&nl->priv->dump_ipv4_defaults, g_hash_table_unref); |
996 | 0 | g_clear_pointer (&nl->priv->dump_ipv6_defaults, g_hash_table_unref); |
997 | 0 | g_clear_object (&nl->priv->ipv4_default_mask); |
998 | 0 | g_clear_object (&nl->priv->ipv6_default_mask); |
999 | |
|
1000 | 0 | G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object); |
1001 | 0 | } |
1002 | | |
1003 | | static void |
1004 | | g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class) |
1005 | 0 | { |
1006 | 0 | GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class); |
1007 | |
|
1008 | 0 | gobject_class->finalize = g_network_monitor_netlink_finalize; |
1009 | 0 | } |
1010 | | |
1011 | | static void |
1012 | | g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface) |
1013 | 0 | { |
1014 | 0 | monitor_iface->can_reach = g_network_monitor_netlink_can_reach; |
1015 | 0 | monitor_iface->can_reach_async = g_network_monitor_netlink_can_reach_async; |
1016 | 0 | monitor_iface->can_reach_finish = g_network_monitor_netlink_can_reach_finish; |
1017 | 0 | } |
1018 | | |
1019 | | static void |
1020 | | g_network_monitor_netlink_initable_iface_init (GInitableIface *iface) |
1021 | 0 | { |
1022 | 0 | initable_parent_iface = g_type_interface_peek_parent (iface); |
1023 | |
|
1024 | 0 | iface->init = g_network_monitor_netlink_initable_init; |
1025 | 0 | } |