• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 20684862027

03 Jan 2026 10:26PM UTC coverage: 72.702% (+0.03%) from 72.677%
20684862027

push

github

web-flow
core/dynamic-user: two trivial modernizations (#40264)

2 of 4 new or added lines in 1 file covered. (50.0%)

215 existing lines in 37 files now uncovered.

310139 of 426587 relevant lines covered (72.7%)

1143601.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.39
/src/network/networkd-dhcp-server.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/if_arp.h>
4
#include <netinet/in.h>
5

6
#include "sd-dhcp-server.h"
7

8
#include "conf-parser.h"
9
#include "dhcp-protocol.h"
10
#include "dhcp-server-lease-internal.h"
11
#include "dns-domain.h"
12
#include "errno-util.h"
13
#include "extract-word.h"
14
#include "fd-util.h"
15
#include "fileio.h"
16
#include "hashmap.h"
17
#include "hostname-setup.h"
18
#include "network-common.h"
19
#include "networkd-address.h"
20
#include "networkd-dhcp-server.h"
21
#include "networkd-dhcp-server-bus.h"
22
#include "networkd-dhcp-server-static-lease.h"
23
#include "networkd-link.h"
24
#include "networkd-manager.h"
25
#include "networkd-network.h"
26
#include "networkd-ntp.h"
27
#include "networkd-queue.h"
28
#include "networkd-resolve-hook.h"
29
#include "networkd-route-util.h"
30
#include "path-util.h"
31
#include "set.h"
32
#include "socket-netlink.h"
33
#include "string-table.h"
34
#include "string-util.h"
35
#include "strv.h"
36

37
static bool link_dhcp4_server_enabled(Link *link) {
883✔
38
        assert(link);
883✔
39

40
        if (link->flags & IFF_LOOPBACK)
883✔
41
                return false;
42

43
        if (!link->network)
883✔
44
                return false;
45

46
        if (link->iftype == ARPHRD_CAN)
883✔
47
                return false;
48

49
        return link->network->dhcp_server;
883✔
50
}
51

52
int network_adjust_dhcp_server(Network *network, Set **addresses) {
6,514✔
53
        int r;
6,514✔
54

55
        assert(network);
6,514✔
56
        assert(addresses);
6,514✔
57

58
        if (!network->dhcp_server)
6,514✔
59
                return 0;
60

61
        if (network->bond) {
3,029✔
62
                log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.",
×
63
                            network->filename);
64
                network->dhcp_server = false;
×
65
                return 0;
×
66
        }
67

68
        assert(network->dhcp_server_address_prefixlen <= 32);
3,029✔
69

70
        if (network->dhcp_server_address_prefixlen == 0) {
3,029✔
71
                Address *address;
3,013✔
72

73
                /* If the server address is not specified, then find suitable static address. */
74

75
                ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) {
3,013✔
76
                        assert(!section_is_invalid(address->section));
3,013✔
77

78
                        if (address->family != AF_INET)
3,013✔
79
                                continue;
×
80

81
                        if (in4_addr_is_localhost(&address->in_addr.in))
3,013✔
82
                                continue;
×
83

84
                        if (in4_addr_is_link_local(&address->in_addr.in))
3,013✔
85
                                continue;
×
86

87
                        if (in4_addr_is_set(&address->in_addr_peer.in))
3,013✔
88
                                continue;
×
89

90
                        /* TODO: check if the prefix length is small enough for the pool. */
91

92
                        network->dhcp_server_address = address;
3,013✔
93
                        address->used_by_dhcp_server = true;
3,013✔
94
                        break;
3,013✔
95
                }
96
                if (!network->dhcp_server_address) {
3,013✔
97
                        log_warning("%s: DHCPServer= is enabled, but no suitable static address configured. "
×
98
                                    "Disabling DHCP server.",
99
                                    network->filename);
100
                        network->dhcp_server = false;
×
101
                        return 0;
×
102
                }
103

104
        } else {
105
                _cleanup_(address_unrefp) Address *a = NULL;
1✔
106
                Address *existing;
16✔
107
                unsigned line;
16✔
108

109
                /* TODO: check if the prefix length is small enough for the pool. */
110

111
                /* If an address is explicitly specified, then check if the corresponding [Address] section
112
                 * is configured, and add one if not. */
113

114
                existing = set_get(*addresses,
32✔
115
                                   &(Address) {
16✔
116
                                           .family = AF_INET,
117
                                           .in_addr.in = network->dhcp_server_address_in_addr,
118
                                           .prefixlen = network->dhcp_server_address_prefixlen,
119
                                   });
120
                if (existing) {
16✔
121
                        /* Corresponding [Address] section already exists. */
122
                        network->dhcp_server_address = existing;
1✔
123
                        return 0;
1✔
124
                }
125

126
                r = ordered_hashmap_by_section_find_unused_line(network->addresses_by_section, network->filename, &line);
15✔
127
                if (r < 0)
15✔
128
                        return log_warning_errno(r, "%s: Failed to find unused line number for DHCP server address: %m",
×
129
                                                 network->filename);
130

131
                r = address_new_static(network, network->filename, line, &a);
15✔
132
                if (r < 0)
15✔
133
                        return log_warning_errno(r, "%s: Failed to add new static address object for DHCP server: %m",
×
134
                                                 network->filename);
135

136
                a->family = AF_INET;
15✔
137
                a->prefixlen = network->dhcp_server_address_prefixlen;
15✔
138
                a->in_addr.in = network->dhcp_server_address_in_addr;
15✔
139
                a->requested_as_null = !in4_addr_is_set(&network->dhcp_server_address_in_addr);
15✔
140
                a->used_by_dhcp_server = true;
15✔
141

142
                r = address_section_verify(a);
15✔
143
                if (r < 0)
15✔
144
                        return r;
145

146
                r = set_ensure_put(addresses, &address_hash_ops, a);
15✔
147
                if (r < 0)
15✔
148
                        return log_oom();
×
149
                assert(r > 0);
15✔
150

151
                network->dhcp_server_address = TAKE_PTR(a);
15✔
152
        }
153

154
        return 0;
155
}
156

157
static DHCPServerPersistLeases link_get_dhcp_server_persist_leases(Link *link) {
60✔
158
        assert(link);
60✔
159
        assert(link->manager);
60✔
160
        assert(link->network);
60✔
161

162
        if (in4_addr_is_set(&link->network->dhcp_server_relay_target))
60✔
163
                return DHCP_SERVER_PERSIST_LEASES_NO; /* On relay mode. Nothing saved in the persistent storage. */
164

165
        if (link->network->dhcp_server_persist_leases >= 0)
58✔
166
                return link->network->dhcp_server_persist_leases;
167

168
        return link->manager->dhcp_server_persist_leases;
49✔
169
}
170

171
static int link_get_dhcp_server_lease_file(Link *link, int *ret_dir_fd, char **ret_path) {
53✔
172
        assert(link);
53✔
173
        assert(link->ifname);
53✔
174
        assert(ret_dir_fd);
53✔
175
        assert(ret_path);
53✔
176

177
        /* This does not copy fd. Do not close fd stored in ret_dir_fd. */
178

179
        switch (link_get_dhcp_server_persist_leases(link)) {
53✔
180
        case DHCP_SERVER_PERSIST_LEASES_NO:
4✔
181
                *ret_dir_fd = -EBADF;
4✔
182
                *ret_path = NULL;
4✔
183
                return 0;
4✔
184

185
        case DHCP_SERVER_PERSIST_LEASES_YES: {
43✔
186
                if (link->manager->persistent_storage_fd < 0)
43✔
187
                        return -EBUSY; /* persistent storage is not ready. */
188

189
                char *p = path_join("dhcp-server-lease", link->ifname);
21✔
190
                if (!p)
21✔
191
                        return -ENOMEM;
192

193
                *ret_dir_fd = link->manager->persistent_storage_fd;
21✔
194
                *ret_path = p;
21✔
195
                return 1;
21✔
196
        }
197
        case DHCP_SERVER_PERSIST_LEASES_RUNTIME: {
6✔
198
                char *p = path_join("/run/systemd/netif/dhcp-server-lease", link->ifname);
6✔
199
                if (!p)
6✔
200
                        return -ENOMEM;
201

202
                *ret_dir_fd = AT_FDCWD;
6✔
203
                *ret_path = p;
6✔
204
                return 1;
6✔
205
        }
206
        default:
×
207
                assert_not_reached();
×
208
        }
209
}
210

211
int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) {
59✔
212
        int r;
59✔
213

214
        assert(link);
59✔
215
        assert(link->manager);
59✔
216
        assert(address);
59✔
217
        assert(ret);
59✔
218

219
        /* If the DHCP server address is configured as a null address, reuse the server address of the
220
         * previous instance. */
221
        if (address->family != AF_INET)
59✔
222
                return -ENOENT;
59✔
223

224
        if (!address->used_by_dhcp_server)
42✔
225
                return -ENOENT;
226

227
        if (!link_dhcp4_server_enabled(link))
26✔
228
                return -ENOENT;
229

230
        _cleanup_free_ char *lease_file = NULL;
26✔
231
        int dir_fd;
26✔
232
        r = link_get_dhcp_server_lease_file(link, &dir_fd, &lease_file);
26✔
233
        if (r < 0)
26✔
234
                return r;
235
        if (r == 0) /* persistent leases is disabled */
4✔
236
                return -ENOENT;
237

238
        struct in_addr a;
4✔
239
        uint8_t prefixlen;
4✔
240
        r = dhcp_server_leases_file_get_server_address(
4✔
241
                        dir_fd,
242
                        lease_file,
243
                        &a,
244
                        &prefixlen);
245
        if (r == -ENOENT)
4✔
246
                return r;
247
        if (r < 0)
1✔
248
                return log_warning_errno(r, "Failed to load lease file %s: %s",
×
249
                                         lease_file,
250
                                         r == -ENXIO ? "expected JSON content not found" :
251
                                         r == -EINVAL ? "invalid JSON" :
252
                                         STRERROR(r));
253

254
        if (prefixlen != address->prefixlen)
1✔
255
                return -ENOENT;
256

257
        ret->in = a;
1✔
258
        return 0;
1✔
259
}
260

261
int link_start_dhcp4_server(Link *link) {
974✔
262
        int r;
974✔
263

264
        assert(link);
974✔
265
        assert(link->manager);
974✔
266

267
        if (!link->dhcp_server)
974✔
268
                return 0; /* Not configured yet. */
974✔
269

270
        if (!link_has_carrier(link))
27✔
271
                return 0;
272

273
        if (sd_dhcp_server_is_running(link->dhcp_server))
27✔
274
                return 0; /* already started. */
275

276
        /* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then
277
         * the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly
278
         * handled as expired and dropped. */
279
        _cleanup_free_ char *lease_file = NULL;
27✔
280
        int dir_fd;
27✔
281
        r = link_get_dhcp_server_lease_file(link, &dir_fd, &lease_file);
27✔
282
        if (r == -EBUSY)
27✔
283
                return 0; /* persistent storage is not ready. */
284
        if (r < 0)
27✔
285
                return r;
286
        if (r > 0) {
27✔
287
                r = sd_dhcp_server_set_lease_file(link->dhcp_server, dir_fd, lease_file);
23✔
288
                if (r < 0)
23✔
289
                        return r;
290
        }
291

292
        r = sd_dhcp_server_start(link->dhcp_server);
27✔
293
        if (r < 0)
27✔
294
                return r;
295

296
        log_link_debug(link, "Offering DHCPv4 leases");
27✔
297
        return 0;
298
}
299

300
void manager_toggle_dhcp4_server_state(Manager *manager, bool start) {
892✔
301
        Link *link;
892✔
302
        int r;
892✔
303

304
        assert(manager);
892✔
305

306
        HASHMAP_FOREACH(link, manager->links_by_index) {
5,224✔
307
                if (!link->dhcp_server)
4,332✔
308
                        continue;
4,325✔
309
                if (link_get_dhcp_server_persist_leases(link) != DHCP_SERVER_PERSIST_LEASES_YES)
7✔
310
                        continue;
6✔
311

312
                /* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set
313
                 * the lease file in link_start_dhcp4_server(). */
314
                r = sd_dhcp_server_stop(link->dhcp_server);
1✔
315
                if (r < 0)
1✔
316
                        log_link_debug_errno(link, r, "Failed to stop DHCP server, ignoring: %m");
×
317

318
                if (!start)
1✔
319
                        continue;
1✔
320

UNCOV
321
                r = link_start_dhcp4_server(link);
×
UNCOV
322
                if (r < 0)
×
323
                        log_link_debug_errno(link, r, "Failed to start DHCP server, ignoring: %m");
×
324
        }
325
}
892✔
326

327
static int dhcp_server_find_uplink(Link *link, Link **ret) {
55✔
328
        assert(link);
55✔
329

330
        if (link->network->dhcp_server_uplink_name)
55✔
331
                return link_get_by_name(link->manager, link->network->dhcp_server_uplink_name, ret);
3✔
332

333
        if (link->network->dhcp_server_uplink_index > 0)
52✔
334
                return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret);
×
335

336
        if (link->network->dhcp_server_uplink_index == UPLINK_INDEX_AUTO) {
52✔
337
                /* It is not necessary to propagate error in automatic selection. */
338
                if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0)
52✔
339
                        *ret = NULL;
50✔
340
                return 0;
52✔
341
        }
342

343
        *ret = NULL;
×
344
        return 0;
×
345
}
346

347
static int link_push_uplink_to_dhcp_server(
4✔
348
                Link *link,
349
                sd_dhcp_lease_server_type_t what,
350
                sd_dhcp_server *s) {
351

352
        _cleanup_free_ struct in_addr *addresses = NULL;
4✔
353
        bool use_dhcp_lease_data = true;
4✔
354
        size_t n_addresses = 0;
4✔
355

356
        assert(link);
4✔
357

358
        if (!link->network)
4✔
359
                return 0;
360
        assert(link->network);
4✔
361

362
        log_link_debug(link, "Copying %s from link", dhcp_lease_server_type_to_string(what));
4✔
363

364
        switch (what) {
4✔
365

366
        case SD_DHCP_LEASE_DNS:
367
                /* For DNS we have a special case. We the data configured explicitly locally along with the
368
                 * data from the DHCP lease. */
369

370
                for (unsigned i = 0; i < link->network->n_dns; i++) {
2✔
371
                        struct in_addr ia;
1✔
372

373
                        /* Only look for IPv4 addresses */
374
                        if (link->network->dns[i]->family != AF_INET)
1✔
375
                                continue;
×
376

377
                        ia = link->network->dns[i]->address.in;
1✔
378

379
                        /* Never propagate obviously borked data */
380
                        if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
1✔
381
                                continue;
×
382

383
                        if (!GREEDY_REALLOC(addresses, n_addresses + 1))
1✔
384
                                return log_oom();
×
385

386
                        addresses[n_addresses++] = ia;
1✔
387
                }
388

389
                use_dhcp_lease_data = link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4);
1✔
390
                break;
391

392
        case SD_DHCP_LEASE_NTP: {
1✔
393
                /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot
394
                 * propagate via DHCP. Hence let's only propagate those which are IP addresses. */
395

396
                STRV_FOREACH(i, link->network->ntp) {
2✔
397
                        union in_addr_union ia;
1✔
398

399
                        if (in_addr_from_string(AF_INET, *i, &ia) < 0)
1✔
400
                                continue;
×
401

402
                        /* Never propagate obviously borked data */
403
                        if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
1✔
404
                                continue;
×
405

406
                        if (!GREEDY_REALLOC(addresses, n_addresses + 1))
1✔
407
                                return log_oom();
×
408

409
                        addresses[n_addresses++] = ia.in;
1✔
410
                }
411

412
                use_dhcp_lease_data = link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4);
1✔
413
                break;
414
        }
415

416
        case SD_DHCP_LEASE_SIP:
2✔
417

418
                /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */
419
                use_dhcp_lease_data = link->network->dhcp_use_sip;
2✔
420
                break;
2✔
421

422
        case SD_DHCP_LEASE_POP3:
423
        case SD_DHCP_LEASE_SMTP:
424
        case SD_DHCP_LEASE_LPR:
425
                /* For the other server types we currently do not allow local configuration of server data,
426
                 * since there are typically no local consumers of the data. */
427
                break;
428

429
        default:
×
430
                assert_not_reached();
×
431
        }
432

433
        if (use_dhcp_lease_data && link->dhcp_lease) {
4✔
434
                const struct in_addr *da;
1✔
435

436
                int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
1✔
437
                if (n > 0) {
1✔
438
                        if (!GREEDY_REALLOC(addresses, n_addresses + n))
×
439
                                return log_oom();
×
440

441
                        for (int j = 0; j < n; j++)
×
442
                                if (in4_addr_is_non_local(&da[j]))
×
443
                                        addresses[n_addresses++] = da[j];
×
444
                }
445
        }
446

447
        if (n_addresses <= 0)
4✔
448
                return 0;
449

450
        return sd_dhcp_server_set_servers(s, what, addresses, n_addresses);
2✔
451
}
452

453
static int dhcp4_server_parse_dns_server_string_and_warn(
42✔
454
                const char *string,
455
                struct in_addr **addresses,
456
                size_t *n_addresses) {
457

458
        for (;;) {
84✔
459
                _cleanup_free_ char *word = NULL, *server_name = NULL;
42✔
460
                union in_addr_union address;
84✔
461
                int family, r, ifindex = 0;
84✔
462

463
                r = extract_first_word(&string, &word, NULL, 0);
84✔
464
                if (r < 0)
84✔
465
                        return r;
466
                if (r == 0)
84✔
467
                        break;
468

469
                r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
42✔
470
                if (r < 0) {
42✔
471
                        log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word);
×
472
                        continue;
×
473
                }
474

475
                /* Only look for IPv4 addresses */
476
                if (family != AF_INET)
42✔
477
                        continue;
21✔
478

479
                /* Never propagate obviously borked data */
480
                if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in))
21✔
481
                        continue;
×
482

483
                if (!GREEDY_REALLOC(*addresses, *n_addresses + 1))
21✔
484
                        return log_oom();
×
485

486
                (*addresses)[(*n_addresses)++] = address.in;
21✔
487
        }
488

489
        return 0;
42✔
490
}
491

492
static int dhcp4_server_set_dns_from_resolve_conf(Link *link) {
7✔
493
        _cleanup_free_ struct in_addr *addresses = NULL;
7✔
494
        _cleanup_fclose_ FILE *f = NULL;
7✔
495
        size_t n_addresses = 0;
7✔
496
        int r;
7✔
497

498
        f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re");
7✔
499
        if (!f) {
7✔
500
                if (errno == ENOENT)
×
501
                        return 0;
502

503
                return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m");
×
504
        }
505

506
        for (;;) {
175✔
507
                _cleanup_free_ char *line = NULL;
168✔
508
                const char *a;
175✔
509

510
                r = read_stripped_line(f, LONG_LINE_MAX, &line);
175✔
511
                if (r < 0)
175✔
512
                        return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m");
×
513
                if (r == 0)
175✔
514
                        break;
515

516
                if (IN_SET(*line, '#', ';', 0))
168✔
517
                        continue;
119✔
518

519
                a = first_word(line, "nameserver");
49✔
520
                if (!a)
49✔
521
                        continue;
7✔
522

523
                r = dhcp4_server_parse_dns_server_string_and_warn(a, &addresses, &n_addresses);
42✔
524
                if (r < 0)
42✔
525
                        log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
42✔
526
        }
527

528
        if (n_addresses <= 0)
7✔
529
                return 0;
530

531
        return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses);
7✔
532
}
533

534
static int dhcp_server_set_domain(Link *link) {
27✔
535
        int r;
27✔
536

537
        assert(link);
27✔
538
        assert(link->network);
27✔
539
        assert(link->dhcp_server);
27✔
540

541
        if (!link->network->dhcp_server_emit_domain)
27✔
542
                return 0;
27✔
543

544
        if (link->network->dhcp_server_domain)
×
545
                return sd_dhcp_server_set_domain_name(link->dhcp_server, link->network->dhcp_server_domain);
×
546

547
        /* When domain is not specified, use the domain part of the current hostname. */
548
        _cleanup_free_ char *hostname = NULL;
×
549
        r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &hostname);
×
550
        if (r < 0)
×
551
                return r;
552

553
        const char *domain = hostname;
×
554
        r = dns_name_parent(&domain);
×
555
        if (r < 0)
×
556
                return r;
557

558
        if (isempty(domain))
×
559
                return -ENXIO;
560

561
        return sd_dhcp_server_set_domain_name(link->dhcp_server, domain);
×
562
}
563

564
static int dhcp4_server_configure(Link *link) {
27✔
565
        bool acquired_uplink = false;
27✔
566
        sd_dhcp_option *p;
27✔
567
        DHCPStaticLease *static_lease;
27✔
568
        Link *uplink = NULL;
27✔
569
        Address *address;
27✔
570
        bool bind_to_interface;
27✔
571
        int r;
27✔
572

573
        assert(link);
27✔
574
        assert(link->network);
27✔
575
        assert(link->network->dhcp_server_address);
27✔
576

577
        log_link_debug(link, "Configuring DHCP Server.");
27✔
578

579
        if (link->dhcp_server)
27✔
580
                return -EBUSY;
27✔
581

582
        r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
27✔
583
        if (r < 0)
27✔
584
                return r;
585

586
        r = sd_dhcp_server_attach_event(link->dhcp_server, link->manager->event, 0);
27✔
587
        if (r < 0)
27✔
588
                return r;
589

590
        r = sd_dhcp_server_set_callback(link->dhcp_server, dhcp_server_callback, link);
27✔
591
        if (r < 0)
27✔
592
                return log_link_warning_errno(link, r, "Failed to set callback for DHCPv4 server instance: %m");
×
593

594
        r = address_get(link, link->network->dhcp_server_address, &address);
27✔
595
        if (r < 0)
27✔
596
                return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m");
×
597

598
        /* use the server address' subnet as the pool */
599
        r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
54✔
600
                                          link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
27✔
601
        if (r < 0)
27✔
602
                return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
×
603

604
        if (link->network->dhcp_server_max_lease_time_usec > 0) {
27✔
605
                r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, link->network->dhcp_server_max_lease_time_usec);
×
606
                if (r < 0)
×
607
                        return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
×
608
        }
609

610
        if (link->network->dhcp_server_default_lease_time_usec > 0) {
27✔
611
                r = sd_dhcp_server_set_default_lease_time(link->dhcp_server, link->network->dhcp_server_default_lease_time_usec);
2✔
612
                if (r < 0)
2✔
613
                        return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
×
614
        }
615

616
        r = sd_dhcp_server_set_ipv6_only_preferred_usec(link->dhcp_server, link->network->dhcp_server_ipv6_only_preferred_usec);
27✔
617
        if (r < 0)
27✔
618
                return log_link_error_errno(link, r, "Failed to set IPv6 only preferred time for DHCPv4 server instance: %m");
×
619

620
        r = sd_dhcp_server_set_boot_server_address(link->dhcp_server, &link->network->dhcp_server_boot_server_address);
27✔
621
        if (r < 0)
27✔
622
                return log_link_warning_errno(link, r, "Failed to set boot server address for DHCPv4 server instance: %m");
×
623

624
        r = sd_dhcp_server_set_boot_server_name(link->dhcp_server, link->network->dhcp_server_boot_server_name);
27✔
625
        if (r < 0)
27✔
626
                return log_link_warning_errno(link, r, "Failed to set boot server name for DHCPv4 server instance: %m");
×
627

628
        r = sd_dhcp_server_set_boot_filename(link->dhcp_server, link->network->dhcp_server_boot_filename);
27✔
629
        if (r < 0)
27✔
630
                return log_link_warning_errno(link, r, "Failed to set boot filename for DHCPv4 server instance: %m");
×
631

632
        r = sd_dhcp_server_set_rapid_commit(link->dhcp_server, link->network->dhcp_server_rapid_commit);
27✔
633
        if (r < 0)
27✔
634
                return log_link_warning_errno(link, r, "Failed to %s Rapid Commit support for DHCPv4 server instance: %m",
×
635
                                              enable_disable(link->network->dhcp_server_rapid_commit));
636

637
        for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type++) {
189✔
638

639
                if (!link->network->dhcp_server_emit[type].emit)
162✔
640
                        continue;
81✔
641

642
                if (link->network->dhcp_server_emit[type].n_addresses > 0)
81✔
643
                        /* Explicitly specified servers to emit */
644
                        r = sd_dhcp_server_set_servers(
35✔
645
                                        link->dhcp_server,
646
                                        type,
647
                                        link->network->dhcp_server_emit[type].addresses,
35✔
648
                                        link->network->dhcp_server_emit[type].n_addresses);
649
                else {
650
                        /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */
651
                        if (!acquired_uplink) {
46✔
652
                                (void) dhcp_server_find_uplink(link, &uplink);
27✔
653
                                acquired_uplink = true;
654
                        }
655

656
                        if (uplink && uplink->network)
46✔
657
                                r = link_push_uplink_to_dhcp_server(uplink, type, link->dhcp_server);
4✔
658
                        else if (type == SD_DHCP_LEASE_DNS)
42✔
659
                                r = dhcp4_server_set_dns_from_resolve_conf(link);
7✔
660
                        else {
661
                                log_link_debug(link,
35✔
662
                                               "Not emitting %s on link, couldn't find suitable uplink.",
663
                                               dhcp_lease_server_type_to_string(type));
664
                                continue;
35✔
665
                        }
666
                }
667

668
                if (r < 0)
46✔
669
                        log_link_warning_errno(link, r,
×
670
                                               "Failed to set %s for DHCP server, ignoring: %m",
671
                                               dhcp_lease_server_type_to_string(type));
672
        }
673

674
        if (link->network->dhcp_server_emit_router) {
27✔
675
                r = sd_dhcp_server_set_router(link->dhcp_server, &link->network->dhcp_server_router);
27✔
676
                if (r < 0)
27✔
677
                        return log_link_error_errno(link, r, "Failed to set router address for DHCP server: %m");
×
678
        }
679

680
        r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target);
27✔
681
        if (r < 0)
27✔
682
                return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m");
×
683

684
        bind_to_interface = sd_dhcp_server_is_in_relay_mode(link->dhcp_server) ? false : link->network->dhcp_server_bind_to_interface;
27✔
685
        r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, bind_to_interface);
27✔
686
        if (r < 0)
27✔
687
                return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m");
×
688

689
        r = sd_dhcp_server_set_relay_agent_information(link->dhcp_server, link->network->dhcp_server_relay_agent_circuit_id, link->network->dhcp_server_relay_agent_remote_id);
27✔
690
        if (r < 0)
27✔
691
                return log_link_error_errno(link, r, "Failed to set agent circuit/remote id for DHCP server: %m");
×
692

693
        if (link->network->dhcp_server_emit_timezone) {
27✔
694
                _cleanup_free_ char *buffer = NULL;
27✔
695
                const char *tz = NULL;
27✔
696

697
                if (link->network->dhcp_server_timezone)
27✔
698
                        tz = link->network->dhcp_server_timezone;
699
                else {
700
                        r = get_timezone(&buffer);
26✔
701
                        if (r < 0)
26✔
702
                                log_link_warning_errno(link, r, "Failed to determine timezone, not sending timezone: %m");
×
703
                        else
704
                                tz = buffer;
26✔
705
                }
706

707
                if (tz) {
27✔
708
                        r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
27✔
709
                        if (r < 0)
27✔
710
                                return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m");
×
711
                }
712
        }
713

714
        r = dhcp_server_set_domain(link);
27✔
715
        if (r == -ENXIO)
27✔
716
                log_link_warning_errno(link, r, "Cannot get domain from the current hostname, DHCP server will not emit domain option.");
×
717
        else if (r < 0)
27✔
718
                return log_link_error_errno(link, r, "Failed to set domain name for DHCP server: %m");
×
719

720
        ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) {
27✔
721
                r = sd_dhcp_server_add_option(link->dhcp_server, p);
×
722
                if (r == -EEXIST)
×
723
                        continue;
×
724
                if (r < 0)
×
725
                        return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
×
726
        }
727

728
        ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options) {
27✔
729
                r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p);
×
730
                if (r == -EEXIST)
×
731
                        continue;
×
732
                if (r < 0)
×
733
                        return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
×
734
        }
735

736
        HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) {
34✔
737
                r = sd_dhcp_server_set_static_lease(
14✔
738
                                link->dhcp_server,
739
                                &static_lease->address,
7✔
740
                                static_lease->client_id,
741
                                static_lease->client_id_size,
742
                                static_lease->hostname);
7✔
743
                if (r < 0)
7✔
744
                        return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
×
745
        }
746

747
        r = link_start_dhcp4_server(link);
27✔
748
        if (r < 0)
27✔
749
                return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
×
750

751
        manager_notify_hook_filters(link->manager);
27✔
752

753
        return 0;
754
}
755

756
static bool dhcp_server_is_ready_to_configure(Link *link) {
931✔
757
        Link *uplink = NULL;
931✔
758
        Address *a;
931✔
759

760
        assert(link);
931✔
761
        assert(link->network);
931✔
762
        assert(link->network->dhcp_server_address);
931✔
763

764
        if (!link_is_ready_to_configure(link, /* allow_unmanaged= */ false))
931✔
765
                return false;
931✔
766

767
        if (!link_has_carrier(link))
234✔
768
                return false;
769

770
        if (!link->static_addresses_configured)
234✔
771
                return false;
772

773
        if (address_get(link, link->network->dhcp_server_address, &a) < 0)
28✔
774
                return false;
775

776
        if (!address_is_ready(a))
28✔
777
                return false;
778

779
        if (dhcp_server_find_uplink(link, &uplink) < 0)
28✔
780
                return false;
781

782
        if (uplink && !uplink->network)
28✔
783
                return false;
1✔
784

785
        return true;
786
}
787

788
static int dhcp_server_process_request(Request *req, Link *link, void *userdata) {
931✔
789
        int r;
931✔
790

791
        assert(link);
931✔
792

793
        if (!dhcp_server_is_ready_to_configure(link))
931✔
794
                return 0;
795

796
        r = dhcp4_server_configure(link);
27✔
797
        if (r < 0)
27✔
798
                return log_link_warning_errno(link, r, "Failed to configure DHCP server: %m");
×
799

800
        return 1;
801
}
802

803
int link_request_dhcp_server(Link *link) {
857✔
804
        int r;
857✔
805

806
        assert(link);
857✔
807

808
        if (!link_dhcp4_server_enabled(link))
857✔
809
                return 0;
810

811
        if (link->dhcp_server)
40✔
812
                return 0;
813

814
        log_link_debug(link, "Requesting DHCP server.");
40✔
815
        r = link_queue_request(link, REQUEST_TYPE_DHCP_SERVER, dhcp_server_process_request, NULL);
40✔
816
        if (r < 0)
40✔
817
                return log_link_warning_errno(link, r, "Failed to request configuration of DHCP server: %m");
×
818

819
        return 0;
820
}
821

822
int config_parse_dhcp_server_relay_agent_suboption(
3✔
823
                const char *unit,
824
                const char *filename,
825
                unsigned line,
826
                const char *section,
827
                unsigned section_line,
828
                const char *lvalue,
829
                int ltype,
830
                const char *rvalue,
831
                void *data,
832
                void *userdata) {
833

834
        char **suboption_value = data;
3✔
835

836
        assert(filename);
3✔
837
        assert(lvalue);
3✔
838
        assert(rvalue);
3✔
839

840
        if (isempty(rvalue)) {
3✔
841
                *suboption_value = mfree(*suboption_value);
×
842
                return 0;
×
843
        }
844

845
        const char *p = startswith(rvalue, "string:");
3✔
846
        if (!p) {
3✔
847
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
848
                           "Failed to parse %s=%s'. Invalid format, ignoring.", lvalue, rvalue);
849
                return 0;
×
850
        }
851
        return free_and_strdup(suboption_value, empty_to_null(p));
3✔
852
}
853

854
int config_parse_dhcp_server_emit(
33✔
855
                const char *unit,
856
                const char *filename,
857
                unsigned line,
858
                const char *section,
859
                unsigned section_line,
860
                const char *lvalue,
861
                int ltype,
862
                const char *rvalue,
863
                void *data,
864
                void *userdata) {
865

866
        NetworkDHCPServerEmitAddress *emit = ASSERT_PTR(data);
33✔
867

868
        assert(rvalue);
33✔
869

870
        if (isempty(rvalue)) {
33✔
871
                emit->addresses = mfree(emit->addresses);
×
872
                emit->n_addresses = 0;
×
873
                return 0;
×
874
        }
875

876
        for (const char *p = rvalue;;) {
33✔
877
                _cleanup_free_ char *w = NULL;
88✔
878
                union in_addr_union a;
88✔
879
                int r;
88✔
880

881
                r = extract_first_word(&p, &w, NULL, 0);
88✔
882
                if (r == -ENOMEM)
88✔
883
                        return log_oom();
×
884
                if (r < 0) {
88✔
885
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
886
                                   "Failed to extract word, ignoring: %s", rvalue);
887
                        return 0;
×
888
                }
889
                if (r == 0)
88✔
890
                        return 0;
891

892
                if (streq(w, "_server_address"))
55✔
893
                        a = IN_ADDR_NULL; /* null address will be converted to the server address. */
26✔
894
                else {
895
                        r = in_addr_from_string(AF_INET, w, &a);
29✔
896
                        if (r < 0) {
29✔
897
                                log_syntax(unit, LOG_WARNING, filename, line, r,
×
898
                                           "Failed to parse %s= address '%s', ignoring: %m", lvalue, w);
899
                                continue;
×
900
                        }
901

902
                        if (in4_addr_is_null(&a.in)) {
29✔
903
                                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
904
                                           "Found a null address in %s=, ignoring.", lvalue);
905
                                continue;
×
906
                        }
907
                }
908

909
                if (!GREEDY_REALLOC(emit->addresses, emit->n_addresses + 1))
55✔
910
                        return log_oom();
×
911

912
                emit->addresses[emit->n_addresses++] = a.in;
55✔
913
        }
914
}
915

916
int config_parse_dhcp_server_address(
16✔
917
                const char *unit,
918
                const char *filename,
919
                unsigned line,
920
                const char *section,
921
                unsigned section_line,
922
                const char *lvalue,
923
                int ltype,
924
                const char *rvalue,
925
                void *data,
926
                void *userdata) {
927

928
        Network *network = ASSERT_PTR(userdata);
16✔
929
        union in_addr_union a;
16✔
930
        unsigned char prefixlen;
16✔
931
        int r;
16✔
932

933
        assert(filename);
16✔
934
        assert(lvalue);
16✔
935
        assert(rvalue);
16✔
936

937
        if (isempty(rvalue)) {
16✔
938
                network->dhcp_server_address_in_addr = (struct in_addr) {};
×
939
                network->dhcp_server_address_prefixlen = 0;
×
940
                return 0;
×
941
        }
942

943
        r = in_addr_prefix_from_string(rvalue, AF_INET, &a, &prefixlen);
16✔
944
        if (r < 0) {
16✔
945
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
946
                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
947
                return 0;
×
948
        }
949
        if (in4_addr_is_localhost(&a.in) || in4_addr_is_link_local(&a.in)) {
16✔
950
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
951
                           "DHCP server address cannot be a localhost or link-local address, "
952
                           "ignoring assignment: %s", rvalue);
953
                return 0;
×
954
        }
955

956
        network->dhcp_server_address_in_addr = a.in;
16✔
957
        network->dhcp_server_address_prefixlen = prefixlen;
16✔
958
        return 0;
16✔
959
}
960

961
int config_parse_dhcp_server_ipv6_only_preferred(
1✔
962
                const char *unit,
963
                const char *filename,
964
                unsigned line,
965
                const char *section,
966
                unsigned section_line,
967
                const char *lvalue,
968
                int ltype,
969
                const char *rvalue,
970
                void *data,
971
                void *userdata) {
972

973
        usec_t t, *usec = ASSERT_PTR(data);
1✔
974
        int r;
1✔
975

976
        assert(filename);
1✔
977
        assert(section);
1✔
978
        assert(lvalue);
1✔
979
        assert(rvalue);
1✔
980

981
        if (isempty(rvalue)) {
1✔
982
                *usec = 0;
×
983
                return 0;
×
984
        }
985

986
        r = parse_sec(rvalue, &t);
1✔
987
        if (r < 0) {
1✔
988
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
989
                           "Failed to parse [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
990
                return 0;
×
991
        }
992

993
        if (t < MIN_V6ONLY_WAIT_USEC && !network_test_mode_enabled()) {
1✔
994
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
995
                           "Invalid [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
996
                return 0;
×
997
        }
998

999
        *usec = t;
1✔
1000
        return 0;
1✔
1001
}
1002

1003
static const char* const dhcp_server_persist_leases_table[_DHCP_SERVER_PERSIST_LEASES_MAX] = {
1004
        [DHCP_SERVER_PERSIST_LEASES_NO]      = "no",
1005
        [DHCP_SERVER_PERSIST_LEASES_YES]     = "yes",
1006
        [DHCP_SERVER_PERSIST_LEASES_RUNTIME] = "runtime",
1007
};
1008

1009
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(
3,009✔
1010
                dhcp_server_persist_leases,
1011
                DHCPServerPersistLeases,
1012
                DHCP_SERVER_PERSIST_LEASES_YES);
1013

1014
DEFINE_CONFIG_PARSE_ENUM(
3,009✔
1015
                config_parse_dhcp_server_persist_leases,
1016
                dhcp_server_persist_leases,
1017
                DHCPServerPersistLeases);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc