mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 16:32:18 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			548 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			548 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GIO - GLib Input, Output and Streaming Library
 | |
|  *
 | |
|  * Copyright 2011 Red Hat, Inc.
 | |
|  *
 | |
|  * SPDX-License-Identifier: LGPL-2.1-or-later
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General
 | |
|  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| #if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME)
 | |
| #include <sys/sysctl.h>
 | |
| #endif
 | |
| 
 | |
| #include "gnetworkmonitornetlink.h"
 | |
| #include "gcredentials.h"
 | |
| #include "ginetaddressmask.h"
 | |
| #include "ginitable.h"
 | |
| #include "giomodule-priv.h"
 | |
| #include "glibintl.h"
 | |
| #include "glib/gstdio.h"
 | |
| #include "gnetworkingprivate.h"
 | |
| #include "gnetworkmonitor.h"
 | |
| #include "gsocket.h"
 | |
| #include "gunixcredentialsmessage.h"
 | |
| 
 | |
| /* must come at the end to pick system includes from
 | |
|  * gnetworkingprivate.h */
 | |
| #ifdef HAVE_LINUX_NETLINK_H
 | |
| #include <linux/netlink.h>
 | |
| #include <linux/rtnetlink.h>
 | |
| #endif
 | |
| #if defined(HAVE_NETLINK_NETLINK_H) && defined(HAVE_NETLINK_NETLINK_ROUTE_H)
 | |
| #include <netlink/netlink.h>
 | |
| #include <netlink/netlink_route.h>
 | |
| #endif
 | |
| 
 | |
| static GInitableIface *initable_parent_iface;
 | |
| static void g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *iface);
 | |
| static void g_network_monitor_netlink_initable_iface_init (GInitableIface *iface);
 | |
| 
 | |
| struct _GNetworkMonitorNetlinkPrivate
 | |
| {
 | |
|   GSocket *sock;
 | |
|   GSource *source, *dump_source;
 | |
|   GMainContext *context;
 | |
| 
 | |
|   GPtrArray *dump_networks;
 | |
| };
 | |
| 
 | |
| static gboolean read_netlink_messages (GNetworkMonitorNetlink  *nl,
 | |
|                                        GError                 **error);
 | |
| static gboolean read_netlink_messages_callback (GSocket             *socket,
 | |
|                                                 GIOCondition         condition,
 | |
|                                                 gpointer             user_data);
 | |
| static gboolean request_dump (GNetworkMonitorNetlink  *nl,
 | |
|                               GError                 **error);
 | |
| 
 | |
| #define g_network_monitor_netlink_get_type _g_network_monitor_netlink_get_type
 | |
| G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
 | |
|                          G_ADD_PRIVATE (GNetworkMonitorNetlink)
 | |
|                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
 | |
|                                                 g_network_monitor_netlink_iface_init)
 | |
|                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
 | |
|                                                 g_network_monitor_netlink_initable_iface_init)
 | |
|                          _g_io_modules_ensure_extension_points_registered ();
 | |
|                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
 | |
|                                                          g_define_type_id,
 | |
|                                                          "netlink",
 | |
|                                                          20))
 | |
| 
 | |
| static void
 | |
| g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
 | |
| {
 | |
|   nl->priv = g_network_monitor_netlink_get_instance_private (nl);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| g_network_monitor_netlink_initable_init (GInitable     *initable,
 | |
|                                          GCancellable  *cancellable,
 | |
|                                          GError       **error)
 | |
| {
 | |
|   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
 | |
|   gint sockfd;
 | |
|   struct sockaddr_nl snl;
 | |
| 
 | |
|   /* We create the socket the old-school way because sockaddr_netlink
 | |
|    * can't be represented as a GSocketAddress
 | |
|    */
 | |
|   sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL);
 | |
|   if (sockfd == -1)
 | |
|     {
 | |
|       int errsv = errno;
 | |
|       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 | |
|                    _("Could not create network monitor: %s"),
 | |
|                    g_strerror (errsv));
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   snl.nl_family = AF_NETLINK;
 | |
|   snl.nl_pid = snl.nl_pad = 0;
 | |
|   snl.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
 | |
|   if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
 | |
|     {
 | |
|       int errsv = errno;
 | |
|       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 | |
|                    _("Could not create network monitor: %s"),
 | |
|                    g_strerror (errsv));
 | |
|       (void) g_close (sockfd, NULL);
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   nl->priv->sock = g_socket_new_from_fd (sockfd, error);
 | |
|   if (!nl->priv->sock)
 | |
|     {
 | |
|       g_prefix_error (error, "%s", _("Could not create network monitor: "));
 | |
|       (void) g_close (sockfd, NULL);
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
| #ifdef SO_PASSCRED
 | |
|   if (!g_socket_set_option (nl->priv->sock, SOL_SOCKET, SO_PASSCRED,
 | |
| 			    TRUE, NULL))
 | |
|     {
 | |
|       int errsv = errno;
 | |
|       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
 | |
|                    _("Could not create network monitor: %s"),
 | |
|                    g_strerror (errsv));
 | |
|       return FALSE;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|   /* Request the current state */
 | |
|   if (!request_dump (nl, error))
 | |
|     return FALSE;
 | |
| 
 | |
|   /* And read responses; since we haven't yet marked the socket
 | |
|    * non-blocking, each call will block until a message is received.
 | |
|    */
 | |
|   while (nl->priv->dump_networks)
 | |
|     {
 | |
|       GError *local_error = NULL;
 | |
|       if (!read_netlink_messages (nl, &local_error))
 | |
|         {
 | |
|           g_warning ("%s", local_error->message);
 | |
|           g_clear_error (&local_error);
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   g_socket_set_blocking (nl->priv->sock, FALSE);
 | |
|   nl->priv->context = g_main_context_ref_thread_default ();
 | |
|   nl->priv->source = g_socket_create_source (nl->priv->sock, G_IO_IN, NULL);
 | |
|   g_source_set_callback (nl->priv->source,
 | |
|                          (GSourceFunc) read_netlink_messages_callback, nl, NULL);
 | |
|   g_source_attach (nl->priv->source, nl->priv->context);
 | |
| 
 | |
|   return initable_parent_iface->init (initable, cancellable, error);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| request_dump (GNetworkMonitorNetlink  *nl,
 | |
|               GError                 **error)
 | |
| {
 | |
|   struct nlmsghdr *n;
 | |
|   struct rtgenmsg *gen;
 | |
|   gchar buf[NLMSG_SPACE (sizeof (*gen))];
 | |
| 
 | |
|   memset (buf, 0, sizeof (buf));
 | |
|   n = (struct nlmsghdr*) buf;
 | |
|   n->nlmsg_len = NLMSG_LENGTH (sizeof (*gen));
 | |
|   n->nlmsg_type = RTM_GETROUTE;
 | |
|   n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
 | |
|   n->nlmsg_pid = 0;
 | |
|   gen = NLMSG_DATA (n);
 | |
|   gen->rtgen_family = AF_UNSPEC;
 | |
| 
 | |
|   if (g_socket_send (nl->priv->sock, buf, sizeof (buf),
 | |
|                      NULL, error) < 0)
 | |
|     {
 | |
|       g_prefix_error (error, "%s", _("Could not get network status: "));
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   nl->priv->dump_networks = g_ptr_array_new_with_free_func (g_object_unref);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| timeout_request_dump (gpointer user_data)
 | |
| {
 | |
|   GNetworkMonitorNetlink *nl = user_data;
 | |
| 
 | |
|   g_source_destroy (nl->priv->dump_source);
 | |
|   g_source_unref (nl->priv->dump_source);
 | |
|   nl->priv->dump_source = NULL;
 | |
| 
 | |
|   request_dump (nl, NULL);
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| queue_request_dump (GNetworkMonitorNetlink *nl)
 | |
| {
 | |
|   if (nl->priv->dump_networks)
 | |
|     return;
 | |
| 
 | |
|   if (nl->priv->dump_source)
 | |
|     {
 | |
|       g_source_destroy (nl->priv->dump_source);
 | |
|       g_source_unref (nl->priv->dump_source);
 | |
|     }
 | |
| 
 | |
|   nl->priv->dump_source = g_timeout_source_new_seconds (1);
 | |
|   g_source_set_callback (nl->priv->dump_source,
 | |
|                          (GSourceFunc) timeout_request_dump, nl, NULL);
 | |
|   g_source_attach (nl->priv->dump_source, nl->priv->context);
 | |
| }
 | |
| 
 | |
| static GInetAddressMask *
 | |
| create_inet_address_mask (GSocketFamily  family,
 | |
|                           const guint8  *dest,
 | |
|                           gsize          dest_len)
 | |
| {
 | |
|   GInetAddress *dest_addr;
 | |
|   GInetAddressMask *network;
 | |
| 
 | |
|   if (dest)
 | |
|     dest_addr = g_inet_address_new_from_bytes (dest, family);
 | |
|   else
 | |
|     dest_addr = g_inet_address_new_any (family);
 | |
|   network = g_inet_address_mask_new (dest_addr, dest_len, NULL);
 | |
|   g_object_unref (dest_addr);
 | |
| 
 | |
|   return network;
 | |
| }
 | |
| 
 | |
| static void
 | |
| add_network (GNetworkMonitorNetlink *nl,
 | |
|              GSocketFamily           family,
 | |
|              const guint8           *dest,
 | |
|              gsize                   dest_len)
 | |
| {
 | |
|   GInetAddressMask *network = create_inet_address_mask (family, dest, dest_len);
 | |
|   g_return_if_fail (network != NULL);
 | |
| 
 | |
|   if (nl->priv->dump_networks)
 | |
|     g_ptr_array_add (nl->priv->dump_networks, g_object_ref (network));
 | |
|   else
 | |
|     g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
 | |
| 
 | |
|   g_object_unref (network);
 | |
| }
 | |
| 
 | |
| static void
 | |
| remove_network (GNetworkMonitorNetlink *nl,
 | |
|                 GSocketFamily           family,
 | |
|                 const guint8           *dest,
 | |
|                 gsize                   dest_len)
 | |
| {
 | |
|   GInetAddressMask *network = create_inet_address_mask (family, dest, dest_len);
 | |
|   g_return_if_fail (network != NULL);
 | |
| 
 | |
|   if (nl->priv->dump_networks)
 | |
|     {
 | |
|       GInetAddressMask **dump_networks = (GInetAddressMask **)nl->priv->dump_networks->pdata;
 | |
|       guint i;
 | |
| 
 | |
|       for (i = 0; i < nl->priv->dump_networks->len; i++)
 | |
|         {
 | |
|           if (g_inet_address_mask_equal (network, dump_networks[i]))
 | |
|             g_ptr_array_remove_index_fast (nl->priv->dump_networks, i--);
 | |
|         }
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), network);
 | |
|     }
 | |
| 
 | |
|   g_object_unref (network);
 | |
| }
 | |
| 
 | |
| static void
 | |
| finish_dump (GNetworkMonitorNetlink *nl)
 | |
| {
 | |
|   g_network_monitor_base_set_networks (G_NETWORK_MONITOR_BASE (nl),
 | |
|                                        (GInetAddressMask **)nl->priv->dump_networks->pdata,
 | |
|                                        nl->priv->dump_networks->len);
 | |
|   g_ptr_array_free (nl->priv->dump_networks, TRUE);
 | |
|   nl->priv->dump_networks = NULL;
 | |
| 
 | |
|   /* FreeBSD features "jailing" functionality, which can be approximated to
 | |
|    * Linux namespaces. A jail may or may not share the host's network stack,
 | |
|    * which includes routing tables.
 | |
|    * When jail runs in non-vnet mode and has a shared stack with the host,
 | |
|    * the kernel prevents jailed processes from getting full view on a routing
 | |
|    * table. This makes GNetworkManager believe that we're offline and return
 | |
|    * FALSE for the "available" property.
 | |
|    * To workaround this problem, do the same thing as GNetworkMonitorBase -
 | |
|    * add a fake network of 0 length.
 | |
|    */
 | |
| #ifdef __FreeBSD__
 | |
|   gboolean is_jailed = FALSE;
 | |
|   gsize len = sizeof (is_jailed);
 | |
| 
 | |
|   if (sysctlbyname ("security.jail.jailed", &is_jailed, &len, NULL, 0) != 0)
 | |
|     return;
 | |
| 
 | |
|   if (!is_jailed)
 | |
|     return;
 | |
| 
 | |
|   if (is_jailed && !g_network_monitor_get_network_available (G_NETWORK_MONITOR (nl)))
 | |
|     {
 | |
|       GInetAddressMask *network;
 | |
|       network = g_inet_address_mask_new_from_string ("0.0.0.0/0", NULL);
 | |
|       g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), network);
 | |
|       g_object_unref (network);
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| read_netlink_messages (GNetworkMonitorNetlink  *nl,
 | |
|                        GError                 **error)
 | |
| {
 | |
|   GInputVector iv;
 | |
|   gssize len;
 | |
|   gint flags;
 | |
|   GError *local_error = NULL;
 | |
|   GSocketAddress *addr = NULL;
 | |
|   struct nlmsghdr *msg;
 | |
|   struct rtmsg *rtmsg;
 | |
|   struct rtattr *attr;
 | |
|   struct sockaddr_nl source_sockaddr;
 | |
|   gsize attrlen;
 | |
|   guint8 *dest, *gateway, *oif;
 | |
| 
 | |
|   iv.buffer = NULL;
 | |
|   iv.size = 0;
 | |
| 
 | |
|   flags = MSG_PEEK | MSG_TRUNC;
 | |
|   len = g_socket_receive_message (nl->priv->sock, NULL, &iv, 1,
 | |
|                                   NULL, NULL, &flags, NULL, &local_error);
 | |
|   if (len < 0)
 | |
|     goto done;
 | |
| 
 | |
|   iv.buffer = g_malloc (len);
 | |
|   iv.size = len;
 | |
|   len = g_socket_receive_message (nl->priv->sock, &addr, &iv, 1,
 | |
|                                   NULL, NULL, NULL, NULL, &local_error);
 | |
|   if (len < 0)
 | |
|     goto done;
 | |
| 
 | |
|   if (!g_socket_address_to_native (addr, &source_sockaddr, sizeof (source_sockaddr), &local_error))
 | |
|     goto done;
 | |
| 
 | |
|   /* If the sender port id is 0 (not fakeable) then the message is from the kernel */
 | |
|   if (source_sockaddr.nl_pid != 0)
 | |
|     goto done;
 | |
| 
 | |
|   msg = (struct nlmsghdr *) iv.buffer;
 | |
|   for (; len > 0; msg = NLMSG_NEXT (msg, len))
 | |
|     {
 | |
|       if (!NLMSG_OK (msg, (size_t) len))
 | |
|         {
 | |
|           g_set_error_literal (&local_error,
 | |
|                                G_IO_ERROR,
 | |
|                                G_IO_ERROR_PARTIAL_INPUT,
 | |
|                                "netlink message was truncated; shouldn't happen...");
 | |
|           goto done;
 | |
|         }
 | |
| 
 | |
|       switch (msg->nlmsg_type)
 | |
|         {
 | |
|         case RTM_NEWROUTE:
 | |
|         case RTM_DELROUTE:
 | |
|           rtmsg = NLMSG_DATA (msg);
 | |
| 
 | |
|           if (rtmsg->rtm_family != AF_INET && rtmsg->rtm_family != AF_INET6)
 | |
|             continue;
 | |
|           if (rtmsg->rtm_type == RTN_UNREACHABLE)
 | |
|             continue;
 | |
| 
 | |
|           attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
 | |
|           attr = RTM_RTA (rtmsg);
 | |
|           dest = gateway = oif = NULL;
 | |
|           while (RTA_OK (attr, attrlen))
 | |
|             {
 | |
|               if (attr->rta_type == RTA_DST)
 | |
|                 dest = RTA_DATA (attr);
 | |
|               else if (attr->rta_type == RTA_GATEWAY)
 | |
|                 gateway = RTA_DATA (attr);
 | |
|               else if (attr->rta_type == RTA_OIF)
 | |
|                 oif = RTA_DATA (attr);
 | |
|               attr = RTA_NEXT (attr, attrlen);
 | |
|             }
 | |
| 
 | |
|           if (dest || gateway || oif)
 | |
|             {
 | |
|               /* Unless we're processing the results of a dump, ignore
 | |
|                * IPv6 link-local multicast routes, which are added and
 | |
|                * removed all the time for some reason.
 | |
|                */
 | |
| #define UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL(a)           \
 | |
|               ((a[0] == 0xff) && ((a[1] & 0xf) == 0x2))
 | |
| 
 | |
|               if (!nl->priv->dump_networks &&
 | |
|                   rtmsg->rtm_family == AF_INET6 &&
 | |
|                   rtmsg->rtm_dst_len != 0 &&
 | |
|                   (dest && UNALIGNED_IN6_IS_ADDR_MC_LINKLOCAL (dest)))
 | |
|                 continue;
 | |
| 
 | |
|               if (msg->nlmsg_type == RTM_NEWROUTE)
 | |
|                 add_network (nl, rtmsg->rtm_family, dest, rtmsg->rtm_dst_len);
 | |
|               else
 | |
|                 remove_network (nl, rtmsg->rtm_family, dest, rtmsg->rtm_dst_len);
 | |
|               queue_request_dump (nl);
 | |
|             }
 | |
|           break;
 | |
| 
 | |
|         case NLMSG_DONE:
 | |
|           finish_dump (nl);
 | |
|           goto done;
 | |
| 
 | |
|         case NLMSG_ERROR:
 | |
|           {
 | |
|             struct nlmsgerr *e = NLMSG_DATA (msg);
 | |
| 
 | |
|             g_set_error (&local_error,
 | |
|                          G_IO_ERROR,
 | |
|                          g_io_error_from_errno (-e->error),
 | |
|                          "netlink error: %s",
 | |
|                          g_strerror (-e->error));
 | |
|           }
 | |
|           goto done;
 | |
| 
 | |
|         default:
 | |
|           g_set_error (&local_error,
 | |
|                        G_IO_ERROR,
 | |
|                        G_IO_ERROR_INVALID_DATA,
 | |
|                        "unexpected netlink message %d",
 | |
|                        msg->nlmsg_type);
 | |
|           goto done;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|  done:
 | |
|   g_free (iv.buffer);
 | |
|   g_clear_object (&addr);
 | |
| 
 | |
|   if (local_error != NULL && nl->priv->dump_networks)
 | |
|     finish_dump (nl);
 | |
| 
 | |
|   if (local_error != NULL)
 | |
|     {
 | |
|       g_propagate_prefixed_error (error, local_error, "Error on netlink socket: ");
 | |
|       return FALSE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       return TRUE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_network_monitor_netlink_finalize (GObject *object)
 | |
| {
 | |
|   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
 | |
| 
 | |
|   if (nl->priv->source)
 | |
|     {
 | |
|       g_source_destroy (nl->priv->source);
 | |
|       g_source_unref (nl->priv->source);
 | |
|     }
 | |
| 
 | |
|   if (nl->priv->dump_source)
 | |
|     {
 | |
|       g_source_destroy (nl->priv->dump_source);
 | |
|       g_source_unref (nl->priv->dump_source);
 | |
|     }
 | |
| 
 | |
|   if (nl->priv->sock)
 | |
|     {
 | |
|       g_socket_close (nl->priv->sock, NULL);
 | |
|       g_object_unref (nl->priv->sock);
 | |
|     }
 | |
| 
 | |
|   g_clear_pointer (&nl->priv->context, g_main_context_unref);
 | |
|   g_clear_pointer (&nl->priv->dump_networks, g_ptr_array_unref);
 | |
| 
 | |
|   G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| read_netlink_messages_callback (GSocket      *socket,
 | |
|                                 GIOCondition  condition,
 | |
|                                 gpointer      user_data)
 | |
| {
 | |
|   GError *error = NULL;
 | |
|   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (user_data);
 | |
| 
 | |
|   if (!read_netlink_messages (nl, &error))
 | |
|     {
 | |
|       g_warning ("Error reading netlink message: %s", error->message);
 | |
|       g_clear_error (&error);
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
 | |
| {
 | |
|   GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
 | |
| 
 | |
|   gobject_class->finalize = g_network_monitor_netlink_finalize;
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
 | |
| {
 | |
|   initable_parent_iface = g_type_interface_peek_parent (iface);
 | |
| 
 | |
|   iface->init = g_network_monitor_netlink_initable_init;
 | |
| }
 |