nm-policy.c 108 KB
Newer Older
1
/* SPDX-License-Identifier: GPL-2.0-or-later */
2
/*
3
 * Copyright (C) 2004 - 2013 Red Hat, Inc.
4
 * Copyright (C) 2007 - 2008 Novell, Inc.
Dan Williams's avatar
Dan Williams committed
5
6
 */

7
#include "src/core/nm-default-daemon.h"
8

9
10
#include "nm-policy.h"

11
12
#include <unistd.h>
#include <netdb.h>
Dan Williams's avatar
Dan Williams committed
13

14
15
16
17
#include "libnm-core-intern/nm-core-internal.h"
#include "libnm-platform/nm-platform.h"
#include "libnm-platform/nmp-object.h"

Dan Williams's avatar
Dan Williams committed
18
#include "NetworkManagerUtils.h"
19
20
#include "devices/nm-device.h"
#include "dns/nm-dns-manager.h"
21
#include "nm-act-request.h"
22
#include "nm-auth-utils.h"
23
24
#include "nm-config.h"
#include "nm-dhcp-config.h"
25
#include "nm-dispatcher.h"
26
27
28
29
#include "nm-firewalld-manager.h"
#include "nm-hostname-manager.h"
#include "nm-keep-alive.h"
#include "nm-l3-config-data.h"
30
#include "nm-manager.h"
31
#include "nm-netns.h"
32
33
34
35
36
37
38
#include "nm-setting-connection.h"
#include "nm-setting-ip4-config.h"
#include "nm-utils.h"
#include "settings/nm-agent-manager.h"
#include "settings/nm-settings-connection.h"
#include "settings/nm-settings.h"
#include "vpn/nm-vpn-manager.h"
39

40
/*****************************************************************************/
41

42
43
44
45
46
47
48
NM_GOBJECT_PROPERTIES_DEFINE(NMPolicy,
                             PROP_MANAGER,
                             PROP_SETTINGS,
                             PROP_DEFAULT_IP4_AC,
                             PROP_DEFAULT_IP6_AC,
                             PROP_ACTIVATING_IP4_AC,
                             PROP_ACTIVATING_IP6_AC, );
49

50
typedef struct {
51
52
    NMManager          *manager;
    NMNetns            *netns;
53
54
    NMFirewalldManager *firewalld_manager;
    CList               pending_activation_checks;
55

56
    NMAgentManager *agent_mgr;
57

58
59
    GHashTable *devices;
    GHashTable *pending_active_connections;
60

61
    GSList *pending_secondaries;
62

63
    NMSettings *settings;
64

65
    NMHostnameManager *hostname_manager;
66

67
68
    NMActiveConnection *default_ac4, *activating_ac4;
    NMActiveConnection *default_ac6, *activating_ac6;
69

70
    NMDnsManager *dns_manager;
71
    gulong        config_changed_id;
72

73
    guint reset_retries_id; /* idle handler for resetting the retries count */
74

75
    guint schedule_activate_all_id; /* idle handler for schedule_activate_all(). */
76

77
    NMPolicyHostnameMode hostname_mode;
78
79
    char                *orig_hostname; /* hostname at NM start time */
    char                *cur_hostname;  /* hostname we want to assign */
80
81
    char *
        last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */
82
83
84
85

    bool changing_hostname : 1; /* hostname set operation in progress */
    bool dhcp_hostname : 1;     /* current hostname was set from dhcp */
    bool updating_dns : 1;
86

87
    GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */
88
89
90
} NMPolicyPrivate;

struct _NMPolicy {
91
92
    GObject         parent;
    NMPolicyPrivate _priv;
93
94
95
};

struct _NMPolicyClass {
96
    GObjectClass parent;
97
};
Dan Winship's avatar
Dan Winship committed
98

99
G_DEFINE_TYPE(NMPolicy, nm_policy, G_TYPE_OBJECT)
100

101
#define NM_POLICY_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPolicy, NM_IS_POLICY)
102
103

static NMPolicy *
104
_PRIV_TO_SELF(NMPolicyPrivate *priv)
105
{
106
    NMPolicy *self;
107

108
    nm_assert(priv);
109

110
    self = (NMPolicy *) (((char *) priv) - G_STRUCT_OFFSET(NMPolicy, _priv));
111

112
113
    nm_assert(NM_IS_POLICY(self));
    return self;
114
}
Dan Winship's avatar
Dan Winship committed
115

116
/*****************************************************************************/
117

118
#define _NMLOG_PREFIX_NAME "policy"
119
120
#undef _NMLOG_ENABLED
#define _NMLOG_ENABLED(level, domain) (nm_logging_enabled((level), (domain)))
121
122
123
124
125
126
127
128
129
130
131
#define _NMLOG(level, domain, ...)                                         \
    G_STMT_START                                                           \
    {                                                                      \
        nm_log((level),                                                    \
               (domain),                                                   \
               NULL,                                                       \
               NULL,                                                       \
               "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__),                    \
               _NMLOG_PREFIX_NAME ": " _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
    }                                                                      \
    G_STMT_END
132
133

/*****************************************************************************/
134

135
static void      update_system_hostname(NMPolicy *self, const char *msg);
136
137
138
static void      schedule_activate_all(NMPolicy *self);
static void      schedule_activate_check(NMPolicy *self, NMDevice *device);
static NMDevice *get_default_device(NMPolicy *self, int addr_family);
139

140
/*****************************************************************************/
141

142
typedef struct {
143
    NMPlatformIP6Address prefix;
144
    NMDevice            *device;      /* The requesting ("uplink") device */
145
    guint64              next_subnet; /* Cache of the next subnet number to be
146
                                       * assigned from this prefix */
147
    GHashTable          *subnets;     /* ifindex -> NMPlatformIP6Address */
148
149
150
} IP6PrefixDelegation;

static void
151
_clear_ip6_subnet(gpointer key, gpointer value, gpointer user_data)
152
{
153
154
    NMPlatformIP6Address *subnet = value;
    NMDevice *device = nm_manager_get_device_by_ifindex(NM_MANAGER_GET, GPOINTER_TO_INT(key));
155

156
157
    if (device) {
        /* We can not remove a subnet we already started announcing.
158
         * Just un-prefer it. */
159
160
161
162
        subnet->preferred = 0;
        nm_device_use_ip6_subnet(device, subnet);
    }
    g_slice_free(NMPlatformIP6Address, subnet);
163
164
165
}

static void
166
clear_ip6_prefix_delegation(gpointer data)
167
{
168
169
    IP6PrefixDelegation *delegation = data;
    char                 sbuf[NM_UTILS_INET_ADDRSTRLEN];
170

171
172
173
174
    _LOGD(LOGD_IP6,
          "ipv6-pd: undelegating prefix %s/%d",
          _nm_utils_inet6_ntop(&delegation->prefix.address, sbuf),
          delegation->prefix.plen);
175

176
177
    g_hash_table_foreach(delegation->subnets, _clear_ip6_subnet, NULL);
    g_hash_table_destroy(delegation->subnets);
178
179
180
}

static void
181
expire_ip6_delegations(NMPolicy *self)
182
{
183
    NMPolicyPrivate     *priv       = NM_POLICY_GET_PRIVATE(self);
184
185
186
    guint32              now        = nm_utils_get_monotonic_timestamp_sec();
    IP6PrefixDelegation *delegation = NULL;
    guint                i;
187

188
189
190
191
192
    for (i = 0; i < priv->ip6_prefix_delegations->len; i++) {
        delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
        if (delegation->prefix.timestamp + delegation->prefix.lifetime < now)
            g_array_remove_index_fast(priv->ip6_prefix_delegations, i);
    }
193
194
195
196
197
198
199
200
201
}

/*
 * Try to obtain a new subnet for a particular active connection from given
 * delegated prefix, possibly reusing the existing subnet.
 * Return value of FALSE indicates no more subnets are available from
 * this prefix (and other prefix should be used -- and requested if necessary).
 */
static gboolean
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
ip6_subnet_from_delegation(IP6PrefixDelegation *delegation, NMDevice *device)
{
    NMPlatformIP6Address *subnet;
    int                   ifindex = nm_device_get_ifindex(device);
    char                  sbuf[NM_UTILS_INET_ADDRSTRLEN];

    subnet = g_hash_table_lookup(delegation->subnets, GINT_TO_POINTER(ifindex));
    if (!subnet) {
        /* Check for out-of-prefixes condition. */
        if (delegation->next_subnet >= (1 << (64 - delegation->prefix.plen))) {
            _LOGD(LOGD_IP6,
                  "ipv6-pd: no more prefixes in %s/%d",
                  _nm_utils_inet6_ntop(&delegation->prefix.address, sbuf),
                  delegation->prefix.plen);
            return FALSE;
        }

        /* Allocate a new subnet. */
        subnet = g_slice_new0(NMPlatformIP6Address);
        g_hash_table_insert(delegation->subnets, GINT_TO_POINTER(ifindex), subnet);

        subnet->plen = 64;
        subnet->address.s6_addr32[0] =
            delegation->prefix.address.s6_addr32[0] | htonl(delegation->next_subnet >> 32);
        subnet->address.s6_addr32[1] =
            delegation->prefix.address.s6_addr32[1] | htonl(delegation->next_subnet);

        /* Out subnet pool management is pretty unsophisticated. We only add
230
231
232
233
         * the subnets and index them by ifindex. That keeps the implementation
         * simple and the dead entries make it easy to reuse the same subnet on
         * subsequent activations. On the other hand they may waste the subnet
         * space. */
234
235
        delegation->next_subnet++;
    }
236

237
238
239
    subnet->timestamp = delegation->prefix.timestamp;
    subnet->lifetime  = delegation->prefix.lifetime;
    subnet->preferred = delegation->prefix.preferred;
240

241
242
243
244
245
    _LOGD(LOGD_IP6,
          "ipv6-pd: %s allocated from a /%d prefix on %s",
          _nm_utils_inet6_ntop(&subnet->address, sbuf),
          delegation->prefix.plen,
          nm_device_get_iface(device));
246

247
    nm_device_use_ip6_subnet(device, subnet);
248

249
    return TRUE;
250
251
252
253
254
255
256
257
}

/*
 * Try to obtain a subnet from each prefix delegated to given requesting
 * ("uplink") device and assign it to the downlink device.
 * Requests a new prefix if no subnet could be found.
 */
static void
258
ip6_subnet_from_device(NMPolicy *self, NMDevice *from_device, NMDevice *device)
259
{
260
    NMPolicyPrivate     *priv          = NM_POLICY_GET_PRIVATE(self);
261
262
263
264
    IP6PrefixDelegation *delegation    = NULL;
    gboolean             got_subnet    = FALSE;
    guint                have_prefixes = 0;
    guint                i;
265

266
    expire_ip6_delegations(self);
267

268
269
    for (i = 0; i < priv->ip6_prefix_delegations->len; i++) {
        delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
270

271
272
        if (delegation->device != from_device)
            continue;
273

274
275
276
277
        if (ip6_subnet_from_delegation(delegation, device))
            got_subnet = TRUE;
        have_prefixes++;
    }
278

279
280
281
282
283
284
285
286
    if (!got_subnet) {
        _LOGI(LOGD_IP6,
              "ipv6-pd: none of %u prefixes of %s can be shared on %s",
              have_prefixes,
              nm_device_get_iface(from_device),
              nm_device_get_iface(device));
        nm_device_request_ip6_prefixes(from_device, have_prefixes + 1);
    }
287
288
289
}

static void
290
ip6_remove_device_prefix_delegations(NMPolicy *self, NMDevice *device)
291
{
292
    NMPolicyPrivate     *priv       = NM_POLICY_GET_PRIVATE(self);
293
294
    IP6PrefixDelegation *delegation = NULL;
    guint                i;
295

296
297
298
299
300
    for (i = 0; i < priv->ip6_prefix_delegations->len; i++) {
        delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
        if (delegation->device == device)
            g_array_remove_index_fast(priv->ip6_prefix_delegations, i);
    }
301
302
303
}

static void
304
device_ip6_prefix_delegated(NMDevice                   *device,
305
306
                            const NMPlatformIP6Address *prefix,
                            gpointer                    user_data)
307
{
308
309
    NMPolicyPrivate     *priv       = user_data;
    NMPolicy            *self       = _PRIV_TO_SELF(priv);
310
311
    IP6PrefixDelegation *delegation = NULL;
    guint                i;
312
313
    const CList         *tmp_list;
    NMActiveConnection  *ac;
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
    char                 sbuf[NM_UTILS_INET_ADDRSTRLEN];

    _LOGI(LOGD_IP6,
          "ipv6-pd: received a prefix %s/%d from %s",
          _nm_utils_inet6_ntop(&prefix->address, sbuf),
          prefix->plen,
          nm_device_get_iface(device));

    expire_ip6_delegations(self);

    for (i = 0; i < priv->ip6_prefix_delegations->len; i++) {
        /* Look for an already known prefix to update. */
        delegation = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
        if (IN6_ARE_ADDR_EQUAL(&delegation->prefix.address, &prefix->address))
            break;
    }

    if (i == priv->ip6_prefix_delegations->len) {
        /* Allocate a delegation for new prefix. */
        g_array_set_size(priv->ip6_prefix_delegations, i + 1);
        delegation          = &g_array_index(priv->ip6_prefix_delegations, IP6PrefixDelegation, i);
        delegation->subnets = g_hash_table_new(nm_direct_hash, NULL);
        delegation->next_subnet = 0;
    }

    delegation->device = device;
    delegation->prefix = *prefix;

342
343
    /* The newly activated connections are added to the end of the list,
     * so traversing it from the end makes it likely for newly
344
345
     * activated connections that have no subnet assigned to be served
     * first. That is a simple yet fair policy, which is good. */
346
    nm_manager_for_each_active_connection_prev (priv->manager, ac, tmp_list) {
347
        NMDevice *to_device;
348

349
350
351
352
        to_device = nm_active_connection_get_device(ac);
        if (nm_device_needs_ip6_subnet(to_device))
            ip6_subnet_from_delegation(delegation, to_device);
    }
353
354
355
}

static void
356
device_ip6_subnet_needed(NMDevice *device, gpointer user_data)
357
{
358
    NMPolicyPrivate *priv = user_data;
359
    NMPolicy        *self = _PRIV_TO_SELF(priv);
360

361
    _LOGD(LOGD_IP6, "ipv6-pd: %s needs a subnet", nm_device_get_iface(device));
362

363
364
365
366
367
368
369
370
371
    if (!priv->default_ac6) {
        /* We request the prefixes when the default IPv6 device is set. */
        _LOGI(LOGD_IP6,
              "ipv6-pd: no device to obtain a subnet to share on %s from",
              nm_device_get_iface(device));
        return;
    }
    ip6_subnet_from_device(self, get_default_device(self, AF_INET6), device);
    nm_device_copy_ip6_dns_config(device, get_default_device(self, AF_INET6));
372
373
374
375
}

/*****************************************************************************/

376
static NMDevice *
377
get_default_device(NMPolicy *self, int addr_family)
378
{
379
    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
380
    NMActiveConnection *ac;
381

382
    nm_assert_addr_family(addr_family);
383

384
    ac = (addr_family == AF_INET) ? priv->default_ac4 : priv->default_ac6;
385

386
    return ac ? nm_active_connection_get_device(ac) : NULL;
387
388
389
}

static NMActiveConnection *
390
get_best_active_connection(NMPolicy *self, int addr_family, gboolean fully_activated)
391
{
392
393
394
    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
    const CList        *tmp_lst;
    NMDevice           *device;
395
396
397
    guint32             best_metric             = G_MAXUINT32;
    gboolean            best_is_fully_activated = FALSE;
    NMActiveConnection *best_ac, *prev_ac;
398

399
    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));
400

401
    /* we prefer the current AC in case of identical metric.
402
     * Hence, try that one first. */
403
404
405
406
407
408
    prev_ac = addr_family == AF_INET ? (fully_activated ? priv->default_ac4 : priv->activating_ac4)
                                     : (fully_activated ? priv->default_ac6 : priv->activating_ac6);
    best_ac = NULL;

    nm_manager_for_each_device (priv->manager, device, tmp_lst) {
        NMDeviceState       state;
409
        const NMPObject    *r;
410
        NMActiveConnection *ac;
411
        NMConnection       *connection;
412
413
414
415
416
417
418
419
420
421
422
423
424
        guint32             metric;
        gboolean            is_fully_activated;

        state = nm_device_get_state(device);
        if (state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING)
            continue;

        if (nm_device_sys_iface_state_is_external(device))
            continue;

        r = nm_device_get_best_default_route(device, addr_family);
        if (r) {
            /* NOTE: the best route might have rt_source NM_IP_CONFIG_SOURCE_VPN,
425
426
427
428
             * which means it was injected by a VPN, not added by device.
             *
             * In this case, is it really the best device? Why do we even need the best
             * device?? */
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
            metric             = NMP_OBJECT_CAST_IP_ROUTE(r)->metric;
            is_fully_activated = TRUE;
        } else if (!fully_activated && (connection = nm_device_get_applied_connection(device))
                   && nm_utils_connection_has_default_route(connection, addr_family, NULL)) {
            metric             = nm_device_get_route_metric(device, addr_family);
            is_fully_activated = FALSE;
        } else
            continue;

        ac = (NMActiveConnection *) nm_device_get_act_request(device);
        nm_assert(ac);

        if (!best_ac || (!best_is_fully_activated && is_fully_activated)
            || (metric < best_metric || (metric == best_metric && ac == prev_ac))) {
            best_ac                 = ac;
            best_metric             = metric;
            best_is_fully_activated = is_fully_activated;
        }
    }

    if (!fully_activated && best_ac && best_is_fully_activated) {
        /* There's a best activating AC only if the best device
451
452
         * among all activating and already-activated devices is a
         * still-activating one. */
453
454
        return NULL;
    }
455

456
    return best_ac;
457
458
}

459
static gboolean
460
all_devices_not_active(NMPolicy *self)
461
{
462
    NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
463
464
    const CList     *tmp_lst;
    NMDevice        *device;
465

466
467
    nm_manager_for_each_device (priv->manager, device, tmp_lst) {
        NMDeviceState state;
468

469
470
471
472
473
474
475
        state = nm_device_get_state(device);
        if (state <= NM_DEVICE_STATE_DISCONNECTED || state >= NM_DEVICE_STATE_DEACTIVATING) {
            continue;
        }
        return FALSE;
    }
    return TRUE;
476
477
}

478
479
#define FALLBACK_HOSTNAME4 "localhost.localdomain"

480
static void
481
482
settings_set_hostname_cb(const char *hostname, gboolean result, gpointer user_data)
{
483
    NMPolicy        *self = NM_POLICY(user_data);
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
    NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE(self);
    int              ret  = 0;
    int              errsv;

    if (!result) {
        _LOGT(LOGD_DNS, "set-hostname: hostname set via dbus failed, fallback to \"sethostname\"");
        ret = sethostname(hostname, strlen(hostname));
        if (ret != 0) {
            errsv = errno;
            _LOGW(LOGD_DNS,
                  "set-hostname: couldn't set the system hostname to '%s': (%d) %s",
                  hostname,
                  errsv,
                  nm_strerror_native(errsv));
            if (errsv == EPERM)
                _LOGW(
                    LOGD_DNS,
                    "set-hostname: you should use hostnamed when systemd hardening is in effect!");
        }
    }

    priv->changing_hostname = FALSE;
    if (!ret)
        nm_dispatcher_call_hostname(NULL, NULL, NULL);
    g_object_unref(self);
509
510
}

511
512
513
#define HOST_NAME_BUFSIZE (HOST_NAME_MAX + 2)

static char *
514
_get_hostname(NMPolicy *self)
515
{
516
    NMPolicyPrivate *priv     = NM_POLICY_GET_PRIVATE(self);
517
    char            *hostname = NULL;
518
    int              errsv;
519

520
    /* If there is an in-progress hostname change, return
521
522
     * the last hostname set as would be set soon...
     */
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    if (priv->changing_hostname) {
        _LOGT(LOGD_DNS, "get-hostname: \"%s\" (last on set)", priv->last_hostname);
        return g_strdup(priv->last_hostname);
    }

    /* try to get the hostname via dbus... */
    if (nm_hostname_manager_get_transient_hostname(priv->hostname_manager, &hostname)) {
        _LOGT(LOGD_DNS, "get-hostname: \"%s\" (from dbus)", hostname);
        return hostname;
    }

    /* ...or retrieve it by yourself */
    hostname = g_malloc(HOST_NAME_BUFSIZE);
    if (gethostname(hostname, HOST_NAME_BUFSIZE - 1) != 0) {
        errsv = errno;
        _LOGT(LOGD_DNS,
              "get-hostname: couldn't get the system hostname: (%d) %s",
              errsv,
              nm_strerror_native(errsv));
        g_free(hostname);
        return NULL;
    }

    /* the name may be truncated... */
    hostname[HOST_NAME_BUFSIZE - 1] = '\0';
    if (strlen(hostname) >= HOST_NAME_BUFSIZE - 1) {
        _LOGT(LOGD_DNS, "get-hostname: system hostname too long: \"%s\"", hostname);
        g_free(hostname);
        return NULL;
    }

    _LOGT(LOGD_DNS, "get-hostname: \"%s\"", hostname);
    return hostname;
556
557
}

558
static void
559
_set_hostname(NMPolicy *self, const char *new_hostname, const char *msg)
560
{
561
    NMPolicyPrivate *priv         = NM_POLICY_GET_PRIVATE(self);
562
563
    gs_free char    *old_hostname = NULL;
    const char      *name;
Dan Winship's avatar
Dan Winship committed
564

565
    /* The incoming hostname *can* be NULL, which will get translated to
566
567
568
569
     * 'localhost.localdomain' or such in the hostname policy code, but we
     * keep cur_hostname = NULL in the case because we need to know that
     * there was no valid hostname to start with.
     */
570

571
    /* Update the DNS only if the hostname is actually
572
573
     * going to change.
     */
574
575
576
    if (!nm_streq0(priv->cur_hostname, new_hostname)) {
        g_free(priv->cur_hostname);
        priv->cur_hostname = g_strdup(new_hostname);
577

578
        /* Notify the DNS manager of the hostname change so that the domain part, if
579
580
         * present, can be added to the search list. Set the @updating_dns flag
         * so that dns_config_changed() doesn't try again to restart DNS lookup.
581
         */
582
        priv->updating_dns = TRUE;
583
584
585
        nm_dns_manager_set_hostname(priv->dns_manager,
                                    priv->cur_hostname,
                                    all_devices_not_active(self));
586
        priv->updating_dns = FALSE;
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
    }

    /* Finally, set kernel hostname */
    if (!new_hostname)
        name = FALLBACK_HOSTNAME4;
    else if (!new_hostname[0]) {
        g_warn_if_reached();
        name = FALLBACK_HOSTNAME4;
    } else
        name = new_hostname;

    /* Don't set the hostname if it isn't actually changing */
    if ((old_hostname = _get_hostname(self)) && (nm_streq(name, old_hostname))) {
        _LOGT(LOGD_DNS, "set-hostname: hostname already set to '%s' (%s)", name, msg);
        return;
    }

    /* Keep track of the last set hostname */
    g_free(priv->last_hostname);
    priv->last_hostname     = g_strdup(name);
    priv->changing_hostname = TRUE;

    _LOGI(LOGD_DNS, "set-hostname: set hostname to '%s' (%s)", name, msg);

    /* Ask NMSettings to update the transient hostname using its
612
     * systemd-hostnamed proxy */
613
614
615
616
    nm_hostname_manager_set_transient_hostname(priv->hostname_manager,
                                               name,
                                               settings_set_hostname_cb,
                                               g_object_ref(self));
617
618
}

619
620
621
622
623
typedef struct {
    NMDevice *device;
    int       priority;
    bool      from_dhcp : 1;
    bool      from_dns : 1;
624
625
    bool      IS_IPv4 : 1;
    bool      is_default : 1;
626
627
628
629
630
631
632
633
634
} DeviceHostnameInfo;

static int
device_hostname_info_compare(gconstpointer a, gconstpointer b)
{
    const DeviceHostnameInfo *info1 = a;
    const DeviceHostnameInfo *info2 = b;

    NM_CMP_FIELD(info1, info2, priority);
635
    NM_CMP_FIELD_UNSAFE(info2, info1, is_default);
636
    NM_CMP_FIELD_UNSAFE(info2, info1, IS_IPv4);
637
638
639
640
641
642
643
644
645
646
647
648
649
650

    return 0;
}

NM_CON_DEFAULT_NOP("hostname.from-dhcp");
NM_CON_DEFAULT_NOP("hostname.from-dns-lookup");
NM_CON_DEFAULT_NOP("hostname.only-from-default");

static gboolean
device_get_hostname_property_boolean(NMDevice *device, const char *name)
{
    NMSettingHostname *s_hostname;
    char               buf[128];
    int                value;
651
    NMTernary          default_value;
652
653
654
655
656
657
658
659
660
661
662
663
664
665

    nm_assert(NM_IN_STRSET(name,
                           NM_SETTING_HOSTNAME_FROM_DHCP,
                           NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP,
                           NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT));

    s_hostname = nm_device_get_applied_setting(device, NM_TYPE_SETTING_HOSTNAME);

    if (s_hostname) {
        g_object_get(s_hostname, name, &value, NULL);
        if (NM_IN_SET(value, NM_TERNARY_FALSE, NM_TERNARY_TRUE))
            return value;
    }

666
667
668
669
670
    if (nm_streq(name, NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT))
        default_value = NM_TERNARY_FALSE;
    else
        default_value = NM_TERNARY_TRUE;

671
672
673
674
675
    return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
                                                       nm_sprintf_buf(buf, "hostname.%s", name),
                                                       device,
                                                       NM_TERNARY_FALSE,
                                                       NM_TERNARY_TRUE,
676
                                                       default_value);
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
}

static int
device_get_hostname_priority(NMDevice *device)
{
    NMSettingHostname *s_hostname;
    int                priority;

    s_hostname = nm_device_get_applied_setting(device, NM_TYPE_SETTING_HOSTNAME);
    if (s_hostname) {
        priority = nm_setting_hostname_get_priority(s_hostname);
        if (priority != 0)
            return priority;
    }

    return nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
                                                       NM_CON_DEFAULT("hostname.priority"),
                                                       device,
                                                       G_MININT,
                                                       G_MAXINT,
                                                       100);
}

static GArray *
build_device_hostname_infos(NMPolicy *self)
{
703
704
    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
    const CList        *tmp_clist;
705
    NMActiveConnection *ac;
706
    GArray             *array = NULL;
707
708
709

    nm_manager_for_each_active_connection (priv->manager, ac, tmp_clist) {
        DeviceHostnameInfo *info;
710
        NMDevice           *device;
711
        gboolean            only_from_default;
712
713
        gboolean            is_default;
        int                 IS_IPv4;
714
715
716
717
718
719
720
721

        device = nm_active_connection_get_device(ac);
        if (!device)
            continue;

        only_from_default =
            device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT);

722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
        for (IS_IPv4 = 1; IS_IPv4 >= 0; IS_IPv4--) {
            is_default = (ac == (IS_IPv4 ? priv->default_ac4 : priv->default_ac6));
            if (only_from_default && !is_default)
                continue;

            if (!array)
                array = g_array_sized_new(FALSE, FALSE, sizeof(DeviceHostnameInfo), 4);

            info  = nm_g_array_append_new(array, DeviceHostnameInfo);
            *info = (DeviceHostnameInfo){
                .device   = device,
                .priority = device_get_hostname_priority(device),
                .from_dhcp =
                    device_get_hostname_property_boolean(device, NM_SETTING_HOSTNAME_FROM_DHCP),
                .from_dns =
                    device_get_hostname_property_boolean(device,
                                                         NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP),
                .IS_IPv4    = IS_IPv4,
                .is_default = is_default,
            };
        }
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
    }

    if (array && array->len > 1) {
        const DeviceHostnameInfo *info0;
        guint                     i;

        g_array_sort(array, device_hostname_info_compare);

        info0 = &g_array_index(array, DeviceHostnameInfo, 0);
        if (info0->priority < 0) {
            for (i = 1; i < array->len; i++) {
                const DeviceHostnameInfo *info = &g_array_index(array, DeviceHostnameInfo, i);

                if (info->priority > info0->priority) {
                    g_array_set_size(array, i);
                    break;
                }
            }
        }
    }

    return array;
}

static void
device_dns_lookup_done(NMDevice *device, gpointer user_data)
{
    NMPolicy *self = user_data;

    g_signal_handlers_disconnect_by_func(device, device_dns_lookup_done, self);

    update_system_hostname(self, "lookup finished");
}

777
static void
778
update_system_hostname(NMPolicy *self, const char *msg)
779
{
780
781
782
783
784
785
    NMPolicyPrivate       *priv = NM_POLICY_GET_PRIVATE(self);
    const char            *configured_hostname;
    gs_free char          *temp_hostname = NULL;
    const char            *dhcp_hostname, *p;
    gboolean               external_hostname = FALSE;
    NMDhcpConfig          *dhcp_config;
786
    gs_unref_array GArray *infos = NULL;
787
    DeviceHostnameInfo    *info;
788
    guint                  i;
789
    int                    addr_family;
790

791
    g_return_if_fail(self != NULL);
792

793
794
795
796
    if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_NONE) {
        _LOGT(LOGD_DNS, "set-hostname: hostname is unmanaged");
        return;
    }
797

798
    _LOGT(LOGD_DNS, "set-hostname: updating hostname (%s)", msg);
799

800
    /* Check if the hostname was set externally to NM, so that in that case
801
802
     * we can avoid to fallback to the one we got when we started.
     * Consider "not specific" hostnames as equal. */
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
    if ((temp_hostname = _get_hostname(self)) && !nm_streq0(temp_hostname, priv->last_hostname)
        && (nm_utils_is_specific_hostname(temp_hostname)
            || nm_utils_is_specific_hostname(priv->last_hostname))) {
        external_hostname = TRUE;
        _LOGI(LOGD_DNS,
              "set-hostname: current hostname was changed outside NetworkManager: '%s'",
              temp_hostname);
        priv->dhcp_hostname = FALSE;

        if (!nm_utils_is_specific_hostname(temp_hostname))
            nm_clear_g_free(&temp_hostname);
        if (!nm_streq0(temp_hostname, priv->orig_hostname)) {
            /* Update original (fallback) hostname */
            g_free(priv->orig_hostname);
            priv->orig_hostname = g_steal_pointer(&temp_hostname);
            _LOGT(LOGD_DNS,
                  "hostname-original: update to %s%s%s",
                  NM_PRINT_FMT_QUOTE_STRING(priv->orig_hostname));
        }
    }

    /* Hostname precedence order:
825
826
     *
     * 1) a configured hostname (from settings)
827
828
829
     * 2) automatic hostname from DHCP of eligible interfaces
     * 3) reverse-DNS lookup of the first address on eligible interfaces
     * 4) the last hostname set outside NM
830
     */
831

832
    /* Try a persistent hostname first */
833
    configured_hostname = nm_hostname_manager_get_static_hostname(priv->hostname_manager);
834
835
836
837
838
839
    if (configured_hostname && nm_utils_is_specific_hostname(configured_hostname)) {
        _set_hostname(self, configured_hostname, "from system configuration");
        priv->dhcp_hostname = FALSE;
        return;
    }

840
841
842
843
844
845
846
    infos = build_device_hostname_infos(self);

    if (infos && _LOGT_ENABLED(LOGD_DNS)) {
        _LOGT(LOGD_DNS, "device hostname info:");
        for (i = 0; i < infos->len; i++) {
            info = &g_array_index(infos, DeviceHostnameInfo, i);
            _LOGT(LOGD_DNS,
847
                  "  - prio:%5d ipv%c%s %s %s dev:%s",
848
                  info->priority,
849
850
851
852
                  info->IS_IPv4 ? '4' : '6',
                  info->is_default ? " (def)" : "      ",
                  info->from_dhcp ? "dhcp " : "     ",
                  info->from_dns ? "dns " : "    ",
853
854
855
856
857
                  nm_device_get_iface(info->device));
        }
    }

    for (i = 0; infos && i < infos->len; i++) {
858
859
        info        = &g_array_index(infos, DeviceHostnameInfo, i);
        addr_family = info->IS_IPv4 ? AF_INET : AF_INET6;
860
        g_signal_handlers_disconnect_by_func(info->device, device_dns_lookup_done, self);
861
862
863
864
865
866
867
868
869
870
871
872
873

        if (info->from_dhcp) {
            dhcp_config = nm_device_get_dhcp_config(info->device, addr_family);
            if (dhcp_config) {
                dhcp_hostname =
                    nm_dhcp_config_get_option(dhcp_config,
                                              info->IS_IPv4 ? "host_name" : "fqdn_fqdn");
                if (dhcp_hostname && dhcp_hostname[0]) {
                    p = nm_str_skip_leading_spaces(dhcp_hostname);
                    if (p[0]) {
                        _set_hostname(self, p, info->IS_IPv4 ? "from DHCPv4" : "from DHCPv6");
                        priv->dhcp_hostname = TRUE;
                        return;
874
                    }
875
876
877
878
879
                    _LOGW(LOGD_DNS,
                          "set-hostname: DHCPv%c-provided hostname '%s' looks invalid; "
                          "ignoring it",
                          nm_utils_addr_family_to_char(addr_family),
                          dhcp_hostname);
880
881
882
883
                }
            }
        }

884
        if (priv->hostname_mode != NM_POLICY_HOSTNAME_MODE_DHCP) {
885
886
887
888
889
890
891
892
893
894
895
896
            if (info->from_dns) {
                const char *result;
                gboolean    wait = FALSE;

                result = nm_device_get_hostname_from_dns_lookup(info->device, addr_family, &wait);
                if (result) {
                    _set_hostname(self, result, "from address lookup");
                    return;
                }
                if (wait) {
                    g_signal_connect(info->device,
                                     NM_DEVICE_DNS_LOOKUP_DONE,
897
                                     G_CALLBACK(device_dns_lookup_done),
898
899
                                     self);
                    return;
900
901
902
903
904
905
906
907
908
909
910
                }
            }
        }
    }

    /* If an hostname was set outside NetworkManager keep it */
    if (external_hostname)
        return;

    if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_DHCP) {
        /* In dhcp hostname-mode, the hostname is updated only if it comes from
911
912
913
914
         * a DHCP host-name option: if last set was from a host-name option and
         * we are here than that connection is gone (with its host-name option),
         * so reset the hostname to the previous value
         */
915
916
917
918
919
920
        if (priv->dhcp_hostname) {
            _set_hostname(self, priv->orig_hostname, "reset dhcp hostname");
            priv->dhcp_hostname = FALSE;
        }
        return;
    }
921

922
    priv->dhcp_hostname = FALSE;
923

924
    /* If no automatically-configured hostname, try using the last hostname
925
926
     * set externally to NM
     */
927
928
929
930
    if (priv->orig_hostname) {
        _set_hostname(self, priv->orig_hostname, "from system startup");
        return;
    }
931

932
    _set_hostname(self, NULL, "no hostname found");
933
934
935
}

static void
936
update_default_ac(NMPolicy *self, int addr_family, NMActiveConnection *best)
937
{
938
939
    NMPolicyPrivate    *priv = NM_POLICY_GET_PRIVATE(self);
    const CList        *tmp_list;
940
    NMActiveConnection *ac;
941

942
    /* Clear the 'default[6]' flag on all active connections that aren't the new
943
944
945
     * default active connection.  We'll set the new default after; this ensures
     * we don't ever have two marked 'default[6]' simultaneously.
     */
946
947
948
949
    nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
        if (ac != best)
            nm_active_connection_set_default(ac, addr_family, FALSE);
    }
950

951
952
953
    /* Mark new default active connection */
    if (best)
        nm_active_connection_set_default(best, addr_family, TRUE);
954
955
}

956
static const NML3ConfigData *
957
get_best_ip_config(NMPolicy            *self,
958
                   int                  addr_family,
959
                   const char         **out_ip_iface,
960
                   NMActiveConnection **out_ac,
961
962
                   NMDevice           **out_device,
                   NMVpnConnection    **out_vpn)
963
{
964
    NMPolicyPrivate      *priv      = NM_POLICY_GET_PRIVATE(self);
965
    const NML3ConfigData *l3cd_best = NULL;
966
967
    const CList          *tmp_list;
    NMActiveConnection   *ac;
968
    guint64               best_metric = G_MAXUINT64;
969
    NMVpnConnection      *best_vpn    = NULL;
970
971
972
973

    nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6));

    nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
974
        const NML3ConfigData *l3cd;
975
        NMVpnConnection      *candidate;
976
        NMVpnConnectionState  vpn_state;
977
        const NMPObject      *obj;
978
        guint32               metric;
979
980
981
982
983
984
985
986
987
988

        if (!NM_IS_VPN_CONNECTION(ac))
            continue;

        candidate = NM_VPN_CONNECTION(ac);

        vpn_state = nm_vpn_connection_get_vpn_state(candidate);
        if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
            continue;

989
990
        l3cd = nm_vpn_connection_get_l3cd(candidate);
        if (!l3cd)
991
992
            continue;

993
        obj = nm_l3_config_data_get_best_default_route(l3cd, addr_family);
994
995
996
997
998
999
        if (!obj)
            continue;

        metric = NMP_OBJECT_CAST_IPX_ROUTE(obj)->rx.metric;
        if (metric <= best_metric) {
            best_metric = metric;
1000
            l3cd_best   = l3cd;