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