Coverage Report

Created: 2025-06-13 06:55

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