LCOV - code coverage report
Current view: top level - gio - gnetworkmonitornetlink.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 65.9 % 405 267
Test Date: 2026-10-06 05:12:46 Functions: 83.3 % 30 25
Branches: - 0 0

             Branch data     Line data    Source code
       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                 :         812 : G_DEFINE_TYPE_WITH_CODE (GNetworkMonitorNetlink, g_network_monitor_netlink, G_TYPE_NETWORK_MONITOR_BASE,
     123                 :             :                          G_ADD_PRIVATE (GNetworkMonitorNetlink)
     124                 :             :                          G_IMPLEMENT_INTERFACE (G_TYPE_NETWORK_MONITOR,
     125                 :             :                                                 g_network_monitor_netlink_iface_init)
     126                 :             :                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
     127                 :             :                                                 g_network_monitor_netlink_initable_iface_init)
     128                 :             :                          _g_io_modules_ensure_extension_points_registered ();
     129                 :             :                          g_io_extension_point_implement (G_NETWORK_MONITOR_EXTENSION_POINT_NAME,
     130                 :             :                                                          g_define_type_id,
     131                 :             :                                                          "netlink",
     132                 :             :                                                          20))
     133                 :             : 
     134                 :             : static void
     135                 :          63 : g_network_monitor_netlink_init (GNetworkMonitorNetlink *nl)
     136                 :             : {
     137                 :          63 :   nl->priv = g_network_monitor_netlink_get_instance_private (nl);
     138                 :          63 : }
     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                 :          84 : create_netlink_socket (guint32   groups,
     144                 :             :                        GError  **error)
     145                 :             : {
     146                 :             :   gint sockfd;
     147                 :             :   struct sockaddr_nl snl;
     148                 :             :   GSocket *sock;
     149                 :          84 :   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                 :          84 :   sockfd = g_socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE, NULL);
     155                 :          84 :   if (sockfd == -1)
     156                 :             :     {
     157                 :           0 :       int errsv = errno;
     158                 :           0 :       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
     159                 :             :                    _("Could not create network monitor: %s"),
     160                 :             :                    g_strerror (errsv));
     161                 :           0 :       return NULL;
     162                 :             :     }
     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                 :          84 :   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                 :          84 :   snl.nl_family = AF_NETLINK;
     171                 :          84 :   snl.nl_pid = snl.nl_pad = 0;
     172                 :          84 :   snl.nl_groups = groups;
     173                 :          84 :   if (bind (sockfd, (struct sockaddr *)&snl, sizeof (snl)) != 0)
     174                 :             :     {
     175                 :           0 :       int errsv = errno;
     176                 :           0 :       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
     177                 :             :                    _("Could not create network monitor: %s"),
     178                 :             :                    g_strerror (errsv));
     179                 :           0 :       (void) g_close (sockfd, NULL);
     180                 :           0 :       return NULL;
     181                 :             :     }
     182                 :             : 
     183                 :          84 :   sock = g_socket_new_from_fd (sockfd, error);
     184                 :          84 :   if (!sock)
     185                 :             :     {
     186                 :           0 :       g_prefix_error (error, "%s", _("Could not create network monitor: "));
     187                 :           0 :       (void) g_close (sockfd, NULL);
     188                 :           0 :       return NULL;
     189                 :             :     }
     190                 :             : 
     191                 :          84 :   return sock;
     192                 :             : }
     193                 :             : 
     194                 :             : static gboolean
     195                 :          21 : setup_monitor (GNetworkMonitorNetlink  *nl,
     196                 :             :                GError                 **error)
     197                 :             : {
     198                 :          21 :   nl->priv->monitor_sock = create_netlink_socket (RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE,
     199                 :             :                                                   error);
     200                 :          21 :   if (!nl->priv->monitor_sock)
     201                 :           0 :     return FALSE;
     202                 :             : 
     203                 :             : #ifdef SO_PASSCRED
     204                 :          21 :   if (!g_socket_set_option (nl->priv->monitor_sock, SOL_SOCKET, SO_PASSCRED,
     205                 :             :                             TRUE, NULL))
     206                 :             :     {
     207                 :           0 :       int errsv = errno;
     208                 :           0 :       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
     209                 :             :                    _("Could not create network monitor: %s"),
     210                 :             :                    g_strerror (errsv));
     211                 :           0 :       return FALSE;
     212                 :             :     }
     213                 :             : #endif
     214                 :             : 
     215                 :          21 :   nl->priv->ipv4_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     216                 :          21 :   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                 :          21 :   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                 :          63 :   while (nl->priv->dumping)
     226                 :             :     {
     227                 :          42 :       GError *local_error = NULL;
     228                 :          42 :       if (!read_netlink_messages (nl, &local_error))
     229                 :             :         {
     230                 :           0 :           g_warning ("%s", local_error->message);
     231                 :           0 :           g_clear_error (&local_error);
     232                 :           0 :           break;
     233                 :             :         }
     234                 :             :     }
     235                 :             : 
     236                 :          21 :   g_socket_set_blocking (nl->priv->monitor_sock, FALSE);
     237                 :          21 :   nl->priv->source = g_socket_create_source (nl->priv->monitor_sock, G_IO_IN, NULL);
     238                 :          21 :   g_source_set_callback (nl->priv->source,
     239                 :             :                          (GSourceFunc) read_netlink_messages_callback, nl, NULL);
     240                 :          21 :   g_source_attach (nl->priv->source, nl->priv->context);
     241                 :             : 
     242                 :          21 :   return TRUE;
     243                 :             : }
     244                 :             : 
     245                 :             : static gboolean
     246                 :          63 : g_network_monitor_netlink_initable_init (GInitable     *initable,
     247                 :             :                                          GCancellable  *cancellable,
     248                 :             :                                          GError       **error)
     249                 :             : {
     250                 :          63 :   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (initable);
     251                 :             : 
     252                 :          63 :   g_mutex_init (&nl->priv->query_lock);
     253                 :          63 :   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                 :          63 :   nl->priv->query_sock = create_netlink_socket (0, error);
     258                 :          63 :   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                 :          84 :   if (G_OBJECT_TYPE (nl) == G_TYPE_NETWORK_MONITOR_NETLINK &&
     264                 :          21 :       !setup_monitor (nl, error))
     265                 :           0 :     return FALSE;
     266                 :             : 
     267                 :          63 :   return initable_parent_iface->init (initable, cancellable, error);
     268                 :             : }
     269                 :             : 
     270                 :             : static gboolean
     271                 :          21 : request_dump (GNetworkMonitorNetlink  *nl,
     272                 :             :               GError                 **error)
     273                 :             : {
     274                 :             :   struct nlmsghdr *n;
     275                 :             :   struct rtmsg *rtm;
     276                 :             :   gchar buf[NLMSG_SPACE (sizeof (*rtm))];
     277                 :             : 
     278                 :             : #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                 :             :   {
     282                 :          21 :     const int strict = 1;
     283                 :          21 :     if (setsockopt (g_socket_get_fd (nl->priv->monitor_sock), SOL_NETLINK,
     284                 :             :                     NETLINK_GET_STRICT_CHK, &strict, sizeof (strict)) != 0)
     285                 :           0 :       g_debug ("Could not set NETLINK_GET_STRICT_CHK: %s", g_strerror (errno));
     286                 :             :   }
     287                 :             : #endif
     288                 :             : 
     289                 :          21 :   memset (buf, 0, sizeof (buf));
     290                 :          21 :   n = (struct nlmsghdr*) buf;
     291                 :          21 :   n->nlmsg_len = NLMSG_LENGTH (sizeof (*rtm));
     292                 :          21 :   n->nlmsg_type = RTM_GETROUTE;
     293                 :          21 :   n->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
     294                 :          21 :   n->nlmsg_pid = 0;
     295                 :          21 :   rtm = NLMSG_DATA (n);
     296                 :          21 :   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                 :          21 :   if (g_socket_send (nl->priv->monitor_sock, buf, sizeof (buf),
     301                 :             :                      NULL, error) < 0)
     302                 :             :     {
     303                 :           0 :       g_prefix_error (error, "%s", _("Could not get network status: "));
     304                 :           0 :       return FALSE;
     305                 :             :     }
     306                 :             : 
     307                 :          21 :   nl->priv->dumping = TRUE;
     308                 :          21 :   nl->priv->dump_ipv4_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     309                 :          21 :   nl->priv->dump_ipv6_defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
     310                 :          21 :   return TRUE;
     311                 :             : }
     312                 :             : 
     313                 :             : static gboolean
     314                 :           0 : timeout_request_dump (gpointer user_data)
     315                 :             : {
     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                 :             : }
     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                 :           0 : queue_request_dump (GNetworkMonitorNetlink *nl)
     332                 :             : {
     333                 :           0 :   if (nl->priv->dumping)
     334                 :           0 :     return;
     335                 :             : 
     336                 :           0 :   if (nl->priv->dump_source)
     337                 :             :     {
     338                 :           0 :       g_source_destroy (nl->priv->dump_source);
     339                 :           0 :       g_source_unref (nl->priv->dump_source);
     340                 :             :     }
     341                 :             : 
     342                 :           0 :   nl->priv->dump_source = g_timeout_source_new_seconds (1);
     343                 :           0 :   g_source_set_callback (nl->priv->dump_source,
     344                 :             :                          (GSourceFunc) timeout_request_dump, nl, NULL);
     345                 :           0 :   g_source_attach (nl->priv->dump_source, nl->priv->context);
     346                 :             : }
     347                 :             : 
     348                 :             : static GInetAddressMask *
     349                 :          21 : default_route_mask (GNetworkMonitorNetlink *nl,
     350                 :             :                     int                     family)
     351                 :             : {
     352                 :             :   GInetAddressMask **maskp;
     353                 :             :   GSocketFamily socket_family;
     354                 :             : 
     355                 :          21 :   if (family == AF_INET)
     356                 :             :     {
     357                 :          21 :       maskp = &nl->priv->ipv4_default_mask;
     358                 :          21 :       socket_family = G_SOCKET_FAMILY_IPV4;
     359                 :             :     }
     360                 :           0 :   else if (family == AF_INET6)
     361                 :             :     {
     362                 :           0 :       maskp = &nl->priv->ipv6_default_mask;
     363                 :           0 :       socket_family = G_SOCKET_FAMILY_IPV6;
     364                 :             :     }
     365                 :             :   else
     366                 :             :     {
     367                 :             :       g_assert_not_reached ();
     368                 :             :     }
     369                 :             : 
     370                 :          21 :   if (*maskp == NULL)
     371                 :             :     {
     372                 :          21 :       GInetAddress *any = g_inet_address_new_any (socket_family);
     373                 :          21 :       *maskp = g_inet_address_mask_new (any, 0, NULL);
     374                 :          21 :       g_object_unref (any);
     375                 :             :     }
     376                 :             : 
     377                 :          21 :   return *maskp;
     378                 :             : }
     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                 :          21 : update_default_route_available (GNetworkMonitorNetlink *nl,
     384                 :             :                                 int                     family,
     385                 :             :                                 gboolean                available)
     386                 :             : {
     387                 :          21 :   GInetAddressMask *mask = default_route_mask (nl, family);
     388                 :             : 
     389                 :          21 :   if (mask == NULL)
     390                 :           0 :     return;
     391                 :             : 
     392                 :          21 :   if (available)
     393                 :          21 :     g_network_monitor_base_add_network (G_NETWORK_MONITOR_BASE (nl), mask);
     394                 :             :   else
     395                 :           0 :     g_network_monitor_base_remove_network (G_NETWORK_MONITOR_BASE (nl), mask);
     396                 :             : }
     397                 :             : 
     398                 :             : static void
     399                 :          21 : add_default_route (GNetworkMonitorNetlink *nl,
     400                 :             :                    int                     family,
     401                 :             :                    gchar                  *stolen_key /* (transfer full) */)
     402                 :             : {
     403                 :             :   GHashTable *set;
     404                 :             : 
     405                 :          21 :   if (nl->priv->dumping)
     406                 :          21 :     set = (family == AF_INET) ? nl->priv->dump_ipv4_defaults : nl->priv->dump_ipv6_defaults;
     407                 :             :   else
     408                 :           0 :     set = (family == AF_INET) ? nl->priv->ipv4_defaults : nl->priv->ipv6_defaults;
     409                 :             : 
     410                 :          21 :   if (!nl->priv->dumping && g_hash_table_size (set) == 0)
     411                 :           0 :     update_default_route_available (nl, family, TRUE);
     412                 :             : 
     413                 :          21 :   g_hash_table_add (set, g_steal_pointer (&stolen_key));
     414                 :          21 : }
     415                 :             : 
     416                 :             : static void
     417                 :           0 : remove_default_route (GNetworkMonitorNetlink *nl,
     418                 :             :                       int                     family,
     419                 :             :                       const gchar            *key)
     420                 :             : {
     421                 :             :   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                 :          21 : finish_dump (GNetworkMonitorNetlink *nl)
     435                 :             : {
     436                 :          21 :   int families[2] = { AF_INET, AF_INET6 };
     437                 :             :   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                 :          63 :   for (i = 0; i < G_N_ELEMENTS (families); i++)
     442                 :             :     {
     443                 :          42 :       int family = families[i];
     444                 :          42 :       GHashTable **livep = (family == AF_INET) ? &nl->priv->ipv4_defaults : &nl->priv->ipv6_defaults;
     445                 :          42 :       GHashTable **dumpp = (family == AF_INET) ? &nl->priv->dump_ipv4_defaults : &nl->priv->dump_ipv6_defaults;
     446                 :          42 :       gboolean was_available = g_hash_table_size (*livep) > 0;
     447                 :          42 :       gboolean now_available = g_hash_table_size (*dumpp) > 0;
     448                 :             : 
     449                 :          42 :       g_hash_table_unref (*livep);
     450                 :          42 :       *livep = g_steal_pointer (dumpp);
     451                 :             : 
     452                 :          42 :       if (was_available != now_available)
     453                 :          21 :         update_default_route_available (nl, family, now_available);
     454                 :             :     }
     455                 :             : 
     456                 :          21 :   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                 :          21 : }
     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                 :          25 : route_type_is_reachable (unsigned char rtm_type)
     492                 :             : {
     493                 :          25 :   return (rtm_type != RTN_UNREACHABLE &&
     494                 :          25 :           rtm_type != RTN_BLACKHOLE &&
     495                 :          50 :           rtm_type != RTN_PROHIBIT &&
     496                 :             :           rtm_type != RTN_THROW);
     497                 :             : }
     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                 :             : #define NETLINK_MESSAGE_MAX_SIZE (64 * 1024)
     504                 :             : 
     505                 :             : static gboolean
     506                 :          42 : read_netlink_messages (GNetworkMonitorNetlink  *nl,
     507                 :             :                        GError                 **error)
     508                 :             : {
     509                 :             :   GInputVector iv;
     510                 :             :   gssize len;
     511                 :             :   gint flags;
     512                 :          42 :   GError *local_error = NULL;
     513                 :          42 :   GSocketAddress *addr = NULL;
     514                 :             :   struct nlmsghdr *msg;
     515                 :             :   struct rtmsg *rtmsg;
     516                 :             :   struct rtattr *attr;
     517                 :             :   struct sockaddr_nl source_sockaddr;
     518                 :             :   gsize attrlen;
     519                 :             :   guint32 table, metric, oif;
     520                 :             :   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                 :          42 :   iv.buffer = g_malloc (NETLINK_MESSAGE_MAX_SIZE);
     525                 :          42 :   iv.size = NETLINK_MESSAGE_MAX_SIZE;
     526                 :          42 :   flags = 0;
     527                 :          42 :   len = g_socket_receive_message (nl->priv->monitor_sock, &addr, &iv, 1,
     528                 :             :                                   NULL, NULL, &flags, NULL, &local_error);
     529                 :          42 :   if (len < 0)
     530                 :           0 :     goto done;
     531                 :          42 :   if (len == 0 || (flags & MSG_TRUNC))
     532                 :             :     {
     533                 :           0 :       g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
     534                 :             :                            "netlink message length is invalid");
     535                 :           0 :       goto done;
     536                 :             :     }
     537                 :             : 
     538                 :          42 :   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                 :          42 :   if (source_sockaddr.nl_pid != 0)
     543                 :           0 :     goto done;
     544                 :             : 
     545                 :          42 :   msg = (struct nlmsghdr *) iv.buffer;
     546                 :         273 :   for (; len > 0; msg = NLMSG_NEXT (msg, len))
     547                 :             :     {
     548                 :         252 :       if (!NLMSG_OK (msg, (size_t) len))
     549                 :             :         {
     550                 :           0 :           g_set_error_literal (&local_error,
     551                 :             :                                G_IO_ERROR,
     552                 :             :                                G_IO_ERROR_PARTIAL_INPUT,
     553                 :             :                                "netlink message was truncated; shouldn't happen...");
     554                 :           0 :           goto done;
     555                 :             :         }
     556                 :             : 
     557                 :         252 :       switch (msg->nlmsg_type)
     558                 :             :         {
     559                 :         231 :         case RTM_NEWROUTE:
     560                 :             :         case RTM_DELROUTE:
     561                 :         231 :           rtmsg = NLMSG_DATA (msg);
     562                 :             : 
     563                 :         231 :           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                 :         231 :           if (rtmsg->rtm_dst_len != 0)
     567                 :         210 :             continue;
     568                 :          21 :           if (!route_type_is_reachable (rtmsg->rtm_type))
     569                 :           0 :             continue;
     570                 :             : 
     571                 :          21 :           table = rtmsg->rtm_table;
     572                 :          21 :           metric = 0;
     573                 :          21 :           oif = 0;
     574                 :          21 :           have_nexthop = FALSE;
     575                 :             : 
     576                 :          21 :           attrlen = NLMSG_PAYLOAD (msg, sizeof (struct rtmsg));
     577                 :          21 :           attr = RTM_RTA (rtmsg);
     578                 :         105 :           while (RTA_OK (attr, attrlen))
     579                 :             :             {
     580                 :          84 :               if (attr->rta_type == RTA_GATEWAY)
     581                 :          21 :                 have_nexthop = TRUE;
     582                 :          63 :               else if (attr->rta_type == RTA_OIF)
     583                 :             :                 {
     584                 :          21 :                   oif = *(guint32 *) RTA_DATA (attr);
     585                 :          21 :                   have_nexthop = TRUE;
     586                 :             :                 }
     587                 :          42 :               else if (attr->rta_type == RTA_PRIORITY)
     588                 :          21 :                 metric = *(guint32 *) RTA_DATA (attr);
     589                 :          21 :               else if (attr->rta_type == RTA_TABLE)
     590                 :          21 :                 table = *(guint32 *) RTA_DATA (attr);
     591                 :           0 :               else if (attr->rta_type == RTA_MULTIPATH)
     592                 :           0 :                 have_nexthop = TRUE;
     593                 :          84 :               attr = RTA_NEXT (attr, attrlen);
     594                 :             :             }
     595                 :             : 
     596                 :             :           /* A default route reaches a nexthop: a gateway, an output interface,
     597                 :             :            * or a set of multipath nexthops. */
     598                 :          21 :           if (have_nexthop)
     599                 :             :             {
     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                 :          21 :               gchar *key = g_strdup_printf ("%u:%u:%u", table, metric, oif);
     604                 :             : 
     605                 :          21 :               if (msg->nlmsg_type == RTM_NEWROUTE)
     606                 :          21 :                 add_default_route (nl, rtmsg->rtm_family, g_steal_pointer (&key));
     607                 :             :               else
     608                 :           0 :                 remove_default_route (nl, rtmsg->rtm_family, key);
     609                 :          21 :               g_free (key);
     610                 :             :             }
     611                 :          21 :           break;
     612                 :             : 
     613                 :          21 :         case NLMSG_DONE:
     614                 :          21 :           finish_dump (nl);
     615                 :          21 :           goto done;
     616                 :             : 
     617                 :           0 :         case NLMSG_ERROR:
     618                 :             :           {
     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                 :             :               {
     625                 :           0 :                 queue_request_dump (nl);
     626                 :           0 :                 goto done;
     627                 :             :               }
     628                 :             : 
     629                 :           0 :             g_set_error (&local_error,
     630                 :             :                          G_IO_ERROR,
     631                 :           0 :                          g_io_error_from_errno (-e->error),
     632                 :             :                          "netlink error: %s",
     633                 :           0 :                          g_strerror (-e->error));
     634                 :             :           }
     635                 :           0 :           goto done;
     636                 :             : 
     637                 :           0 :         default:
     638                 :           0 :           g_set_error (&local_error,
     639                 :             :                        G_IO_ERROR,
     640                 :             :                        G_IO_ERROR_INVALID_DATA,
     641                 :             :                        "unexpected netlink message %d",
     642                 :           0 :                        msg->nlmsg_type);
     643                 :           0 :           goto done;
     644                 :             :         }
     645                 :             :     }
     646                 :             : 
     647                 :          21 :  done:
     648                 :          42 :   g_free (iv.buffer);
     649                 :          42 :   g_clear_object (&addr);
     650                 :             : 
     651                 :          42 :   if (local_error != NULL && nl->priv->dumping)
     652                 :           0 :     finish_dump (nl);
     653                 :             : 
     654                 :          42 :   if (local_error != NULL)
     655                 :             :     {
     656                 :           0 :       g_propagate_prefixed_error (error, local_error, "Error on netlink socket: ");
     657                 :           0 :       return FALSE;
     658                 :             :     }
     659                 :             :   else
     660                 :             :     {
     661                 :          42 :       return TRUE;
     662                 :             :     }
     663                 :             : }
     664                 :             : 
     665                 :             : static gboolean
     666                 :           0 : read_netlink_messages_callback (GSocket      *socket,
     667                 :             :                                 GIOCondition  condition,
     668                 :             :                                 gpointer      user_data)
     669                 :             : {
     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                 :             :     {
     675                 :           0 :       g_warning ("Error reading netlink message: %s", error->message);
     676                 :           0 :       g_clear_error (&error);
     677                 :           0 :       return FALSE;
     678                 :             :     }
     679                 :             : 
     680                 :           0 :   return TRUE;
     681                 :             : }
     682                 :             : 
     683                 :             : /* Map a GSocketFamily to its AF_* address family constant. */
     684                 :             : static int
     685                 :           4 : socket_family_to_af_family (GSocketFamily socket_family)
     686                 :             : {
     687                 :           4 :   switch (socket_family)
     688                 :             :     {
     689                 :           2 :     case G_SOCKET_FAMILY_IPV4:
     690                 :           2 :       return AF_INET;
     691                 :           2 :     case G_SOCKET_FAMILY_IPV6:
     692                 :           2 :       return AF_INET6;
     693                 :           0 :     default:
     694                 :             :       g_assert_not_reached ();
     695                 :             :     }
     696                 :             : }
     697                 :             : 
     698                 :             : /* Map a kernel route-lookup errno to the GIOError reported by can_reach(). */
     699                 :             : static GIOErrorEnum
     700                 :           0 : route_get_error_from_errno (int err)
     701                 :             : {
     702                 :           0 :   switch (err)
     703                 :             :     {
     704                 :           0 :     case ENETUNREACH:
     705                 :             :     case ENETDOWN:
     706                 :           0 :       return G_IO_ERROR_NETWORK_UNREACHABLE;
     707                 :           0 :     case EHOSTUNREACH:
     708                 :             :     case EHOSTDOWN:
     709                 :             :     case EACCES:
     710                 :           0 :       return G_IO_ERROR_HOST_UNREACHABLE;
     711                 :           0 :     default:
     712                 :           0 :       return g_io_error_from_errno (err);
     713                 :             :     }
     714                 :             : }
     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                 :           4 : route_get (GNetworkMonitorNetlink  *nl,
     723                 :             :            GInetAddress            *addr,
     724                 :             :            GCancellable            *cancellable,
     725                 :             :            GError                 **error)
     726                 :             : {
     727                 :           4 :   GSocketFamily socket_family = g_inet_address_get_family (addr);
     728                 :           4 :   int family = socket_family_to_af_family (socket_family);
     729                 :           4 :   gsize addrlen = g_inet_address_get_native_size (addr);
     730                 :           4 :   const guint8 *addrbytes = g_inet_address_to_bytes (addr);
     731                 :             :   struct {
     732                 :             :     struct nlmsghdr n;
     733                 :             :     struct rtmsg    r;
     734                 :             :     gchar           buf[64];
     735                 :             :   } req;
     736                 :             :   struct nlmsghdr *msg;
     737                 :             :   struct rtmsg *rtmsg;
     738                 :             :   struct rtattr *rta;
     739                 :           4 :   GInputVector iv = { NULL, 0 };
     740                 :             :   struct sockaddr_nl source_sockaddr;
     741                 :           4 :   GSocketAddress *src_addr = NULL;
     742                 :           4 :   GError *local_error = NULL;
     743                 :             :   gssize len;
     744                 :             :   gint flags;
     745                 :             :   guint seq;
     746                 :           4 :   gboolean reachable = FALSE;
     747                 :           4 :   gboolean got_reply = FALSE;
     748                 :             : 
     749                 :           4 :   memset (&req, 0, sizeof (req));
     750                 :           4 :   req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
     751                 :           4 :   req.n.nlmsg_type = RTM_GETROUTE;
     752                 :           4 :   req.n.nlmsg_flags = NLM_F_REQUEST;
     753                 :           4 :   req.r.rtm_family = family;
     754                 :           4 :   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                 :             : #ifdef RTM_F_FIB_MATCH
     761                 :           4 :   req.r.rtm_flags = RTM_F_FIB_MATCH;
     762                 :             : #endif
     763                 :             : 
     764                 :           4 :   rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN (req.n.nlmsg_len));
     765                 :           4 :   rta->rta_type = RTA_DST;
     766                 :           4 :   rta->rta_len = RTA_LENGTH (addrlen);
     767                 :           4 :   g_assert ((char *) rta + rta->rta_len <= (char *) &req + sizeof (req));
     768                 :           4 :   memcpy (RTA_DATA (rta), addrbytes, addrlen);
     769                 :           4 :   req.n.nlmsg_len = NLMSG_ALIGN (req.n.nlmsg_len) + rta->rta_len;
     770                 :             : 
     771                 :           4 :   g_mutex_lock (&nl->priv->query_lock);
     772                 :             : 
     773                 :           4 :   seq = ++nl->priv->query_seq;
     774                 :           4 :   req.n.nlmsg_seq = seq;
     775                 :             : 
     776                 :           4 :   if (g_socket_send (nl->priv->query_sock, (const gchar *) &req, req.n.nlmsg_len,
     777                 :             :                      cancellable, &local_error) < 0)
     778                 :           0 :     goto done;
     779                 :             : 
     780                 :           4 :   flags = MSG_PEEK | MSG_TRUNC;
     781                 :           4 :   len = g_socket_receive_message (nl->priv->query_sock, NULL, &iv, 1,
     782                 :             :                                   NULL, NULL, &flags, cancellable, &local_error);
     783                 :           4 :   if (len < 0)
     784                 :           0 :     goto done;
     785                 :           4 :   if (len == 0 || len > NETLINK_MESSAGE_MAX_SIZE)
     786                 :             :     {
     787                 :           0 :       g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
     788                 :             :                            "netlink route reply length is invalid");
     789                 :           0 :       goto done;
     790                 :             :     }
     791                 :             : 
     792                 :           4 :   iv.buffer = g_malloc (len);
     793                 :           4 :   iv.size = len;
     794                 :           4 :   len = g_socket_receive_message (nl->priv->query_sock, &src_addr, &iv, 1,
     795                 :             :                                   NULL, NULL, NULL, cancellable, &local_error);
     796                 :           4 :   if (len < 0)
     797                 :           0 :     goto done;
     798                 :             : 
     799                 :             :   /* Only trust messages from the kernel (sender port id 0). */
     800                 :           4 :   if (!g_socket_address_to_native (src_addr, &source_sockaddr, sizeof (source_sockaddr), &local_error))
     801                 :           0 :     goto done;
     802                 :           4 :   if (source_sockaddr.nl_pid != 0)
     803                 :           0 :     goto done;
     804                 :             : 
     805                 :           4 :   msg = (struct nlmsghdr *) iv.buffer;
     806                 :           4 :   for (; len > 0; msg = NLMSG_NEXT (msg, len))
     807                 :             :     {
     808                 :           4 :       if (!NLMSG_OK (msg, (size_t) len))
     809                 :             :         break;
     810                 :           4 :       if (msg->nlmsg_seq != seq)
     811                 :           0 :         continue;
     812                 :             : 
     813                 :           4 :       if (msg->nlmsg_type == NLMSG_ERROR)
     814                 :             :         {
     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                 :             :         }
     825                 :           4 :       else if (msg->nlmsg_type == RTM_NEWROUTE)
     826                 :             :         {
     827                 :           4 :           rtmsg = NLMSG_DATA (msg);
     828                 :           4 :           got_reply = TRUE;
     829                 :           4 :           reachable = route_type_is_reachable (rtmsg->rtm_type);
     830                 :           4 :           if (!reachable)
     831                 :           0 :             g_set_error_literal (&local_error, G_IO_ERROR,
     832                 :             :                                  G_IO_ERROR_HOST_UNREACHABLE,
     833                 :             :                                  _("Host unreachable"));
     834                 :           4 :           break;
     835                 :             :         }
     836                 :             :     }
     837                 :             : 
     838                 :           0 :  done:
     839                 :           4 :   g_mutex_unlock (&nl->priv->query_lock);
     840                 :             : 
     841                 :           4 :   g_free (iv.buffer);
     842                 :           4 :   g_clear_object (&src_addr);
     843                 :             : 
     844                 :           4 :   if (reachable)
     845                 :             :     {
     846                 :           4 :       g_assert (local_error == NULL);
     847                 :           4 :       return TRUE;
     848                 :             :     }
     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                 :             :                          _("Host unreachable"));
     855                 :           0 :   return FALSE;
     856                 :             : }
     857                 :             : 
     858                 :             : static gboolean
     859                 :           4 : g_network_monitor_netlink_can_reach (GNetworkMonitor     *monitor,
     860                 :             :                                      GSocketConnectable  *connectable,
     861                 :             :                                      GCancellable        *cancellable,
     862                 :             :                                      GError             **error)
     863                 :             : {
     864                 :           4 :   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (monitor);
     865                 :             :   GSocketAddressEnumerator *enumerator;
     866                 :             :   GSocketAddress *addr;
     867                 :           4 :   GError *local_error = NULL;
     868                 :             : 
     869                 :           4 :   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                 :           4 :   while ((addr = g_socket_address_enumerator_next (enumerator, cancellable, &local_error)))
     875                 :             :     {
     876                 :           4 :       if (G_IS_INET_SOCKET_ADDRESS (addr))
     877                 :             :         {
     878                 :             :           GInetAddress *iaddr;
     879                 :             : 
     880                 :           4 :           iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
     881                 :           4 :           if (route_get (nl, iaddr, cancellable, &local_error))
     882                 :             :             {
     883                 :           4 :               g_object_unref (addr);
     884                 :           4 :               g_object_unref (enumerator);
     885                 :           4 :               return TRUE;
     886                 :             :             }
     887                 :             : 
     888                 :           0 :           if (g_cancellable_is_cancelled (cancellable))
     889                 :             :             {
     890                 :           0 :               g_object_unref (addr);
     891                 :           0 :               break;
     892                 :             :             }
     893                 :           0 :           g_clear_error (&local_error);
     894                 :             :         }
     895                 :             : 
     896                 :           0 :       g_object_unref (addr);
     897                 :             :     }
     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                 :             :     {
     903                 :           0 :       g_propagate_error (error, g_steal_pointer (&local_error));
     904                 :           0 :       return FALSE;
     905                 :             :     }
     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                 :             :                        _("Host unreachable"));
     912                 :           0 :   return FALSE;
     913                 :             : }
     914                 :             : 
     915                 :             : static void
     916                 :           2 : can_reach_thread (GTask        *task,
     917                 :             :                   gpointer      source_object,
     918                 :             :                   gpointer      task_data,
     919                 :             :                   GCancellable *cancellable)
     920                 :             : {
     921                 :           2 :   GNetworkMonitor *monitor = G_NETWORK_MONITOR (source_object);
     922                 :           2 :   GSocketConnectable *connectable = G_SOCKET_CONNECTABLE (task_data);
     923                 :           2 :   GError *error = NULL;
     924                 :             : 
     925                 :           2 :   if (g_network_monitor_netlink_can_reach (monitor, connectable, cancellable, &error))
     926                 :           2 :     g_task_return_boolean (task, TRUE);
     927                 :             :   else
     928                 :           0 :     g_task_return_error (task, g_steal_pointer (&error));
     929                 :           2 : }
     930                 :             : 
     931                 :             : static void
     932                 :           2 : g_network_monitor_netlink_can_reach_async (GNetworkMonitor     *monitor,
     933                 :             :                                            GSocketConnectable  *connectable,
     934                 :             :                                            GCancellable        *cancellable,
     935                 :             :                                            GAsyncReadyCallback  callback,
     936                 :             :                                            gpointer             user_data)
     937                 :             : {
     938                 :             :   GTask *task;
     939                 :             : 
     940                 :           2 :   task = g_task_new (monitor, cancellable, callback, user_data);
     941                 :           2 :   g_task_set_source_tag (task, g_network_monitor_netlink_can_reach_async);
     942                 :           2 :   g_task_set_static_name (task, "[gio] network monitor can_reach");
     943                 :           2 :   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                 :           2 :   g_task_run_in_thread (task, can_reach_thread);
     948                 :           2 :   g_object_unref (task);
     949                 :           2 : }
     950                 :             : 
     951                 :             : static gboolean
     952                 :           2 : g_network_monitor_netlink_can_reach_finish (GNetworkMonitor  *monitor,
     953                 :             :                                             GAsyncResult     *result,
     954                 :             :                                             GError          **error)
     955                 :             : {
     956                 :           2 :   g_return_val_if_fail (g_task_is_valid (result, monitor), FALSE);
     957                 :             : 
     958                 :           2 :   return g_task_propagate_boolean (G_TASK (result), error);
     959                 :             : }
     960                 :             : 
     961                 :             : static void
     962                 :          21 : g_network_monitor_netlink_finalize (GObject *object)
     963                 :             : {
     964                 :          21 :   GNetworkMonitorNetlink *nl = G_NETWORK_MONITOR_NETLINK (object);
     965                 :             : 
     966                 :          21 :   if (nl->priv->source)
     967                 :             :     {
     968                 :           0 :       g_source_destroy (nl->priv->source);
     969                 :           0 :       g_source_unref (nl->priv->source);
     970                 :             :     }
     971                 :             : 
     972                 :          21 :   if (nl->priv->dump_source)
     973                 :             :     {
     974                 :           0 :       g_source_destroy (nl->priv->dump_source);
     975                 :           0 :       g_source_unref (nl->priv->dump_source);
     976                 :             :     }
     977                 :             : 
     978                 :          21 :   if (nl->priv->query_sock)
     979                 :             :     {
     980                 :          21 :       g_socket_close (nl->priv->query_sock, NULL);
     981                 :          21 :       g_object_unref (nl->priv->query_sock);
     982                 :             :     }
     983                 :             : 
     984                 :          21 :   if (nl->priv->monitor_sock)
     985                 :             :     {
     986                 :           0 :       g_socket_close (nl->priv->monitor_sock, NULL);
     987                 :           0 :       g_object_unref (nl->priv->monitor_sock);
     988                 :             :     }
     989                 :             : 
     990                 :          21 :   g_mutex_clear (&nl->priv->query_lock);
     991                 :             : 
     992                 :          21 :   g_clear_pointer (&nl->priv->context, g_main_context_unref);
     993                 :          21 :   g_clear_pointer (&nl->priv->ipv4_defaults, g_hash_table_unref);
     994                 :          21 :   g_clear_pointer (&nl->priv->ipv6_defaults, g_hash_table_unref);
     995                 :          21 :   g_clear_pointer (&nl->priv->dump_ipv4_defaults, g_hash_table_unref);
     996                 :          21 :   g_clear_pointer (&nl->priv->dump_ipv6_defaults, g_hash_table_unref);
     997                 :          21 :   g_clear_object (&nl->priv->ipv4_default_mask);
     998                 :          21 :   g_clear_object (&nl->priv->ipv6_default_mask);
     999                 :             : 
    1000                 :          21 :   G_OBJECT_CLASS (g_network_monitor_netlink_parent_class)->finalize (object);
    1001                 :          21 : }
    1002                 :             : 
    1003                 :             : static void
    1004                 :          22 : g_network_monitor_netlink_class_init (GNetworkMonitorNetlinkClass *nl_class)
    1005                 :             : {
    1006                 :          22 :   GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
    1007                 :             : 
    1008                 :          22 :   gobject_class->finalize = g_network_monitor_netlink_finalize;
    1009                 :          22 : }
    1010                 :             : 
    1011                 :             : static void
    1012                 :          22 : g_network_monitor_netlink_iface_init (GNetworkMonitorInterface *monitor_iface)
    1013                 :             : {
    1014                 :          22 :   monitor_iface->can_reach = g_network_monitor_netlink_can_reach;
    1015                 :          22 :   monitor_iface->can_reach_async = g_network_monitor_netlink_can_reach_async;
    1016                 :          22 :   monitor_iface->can_reach_finish = g_network_monitor_netlink_can_reach_finish;
    1017                 :          22 : }
    1018                 :             : 
    1019                 :             : static void
    1020                 :          22 : g_network_monitor_netlink_initable_iface_init (GInitableIface *iface)
    1021                 :             : {
    1022                 :          22 :   initable_parent_iface = g_type_interface_peek_parent (iface);
    1023                 :             : 
    1024                 :          22 :   iface->init = g_network_monitor_netlink_initable_init;
    1025                 :          22 : }
        

Generated by: LCOV version 2.0-1