]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-server.c
cryptsetup-pkcs11: use erase_and_free for decrypted key cleanup.
[thirdparty/systemd.git] / src / network / networkd-dhcp-server.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8fcf1d61 2
5ae0fb7f
YW
3#include <netinet/in.h>
4#include <linux/if_arp.h>
5#include <linux/if.h>
6
8fcf1d61
YW
7#include "sd-dhcp-server.h"
8
dd1d3060
MAL
9#include "fd-util.h"
10#include "fileio.h"
093e3533 11#include "networkd-address.h"
a95e9306 12#include "networkd-dhcp-server-bus.h"
c517a49b 13#include "networkd-dhcp-server-static-lease.h"
14#include "networkd-dhcp-server.h"
8fcf1d61
YW
15#include "networkd-link.h"
16#include "networkd-manager.h"
17#include "networkd-network.h"
564ca984 18#include "parse-util.h"
dd1d3060 19#include "socket-netlink.h"
564ca984
SS
20#include "string-table.h"
21#include "string-util.h"
dd1d3060 22#include "strv.h"
8fcf1d61 23
5ae0fb7f
YW
24static bool link_dhcp4_server_enabled(Link *link) {
25 assert(link);
26
27 if (link->flags & IFF_LOOPBACK)
28 return false;
29
30 if (!link->network)
31 return false;
32
5ae0fb7f
YW
33 if (link->iftype == ARPHRD_CAN)
34 return false;
35
36 return link->network->dhcp_server;
37}
38
0017ba31
YW
39void network_adjust_dhcp_server(Network *network) {
40 assert(network);
41
42 if (!network->dhcp_server)
43 return;
44
45 if (network->bond) {
46 log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.",
47 network->filename);
48 network->dhcp_server = false;
49 return;
50 }
51
52 if (!in4_addr_is_set(&network->dhcp_server_address)) {
53 Address *address;
54 bool have = false;
55
56 ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) {
57 if (section_is_invalid(address->section))
58 continue;
59 if (address->family == AF_INET &&
60 !in4_addr_is_localhost(&address->in_addr.in) &&
61 in4_addr_is_null(&address->in_addr_peer.in)) {
62 have = true;
63 break;
64 }
65 }
66 if (!have) {
67 log_warning("%s: DHCPServer= is enabled, but no static address configured. "
68 "Disabling DHCP server.",
69 network->filename);
70 network->dhcp_server = false;
71 return;
72 }
73 }
74}
75
76static int link_find_dhcp_server_address(Link *link, Address **ret) {
8fcf1d61
YW
77 Address *address;
78
79 assert(link);
80 assert(link->network);
81
0017ba31
YW
82 /* If ServerAddress= is specified, then use the address. */
83 if (in4_addr_is_set(&link->network->dhcp_server_address))
84 return link_get_ipv4_address(link, &link->network->dhcp_server_address,
85 link->network->dhcp_server_address_prefixlen, ret);
8fcf1d61 86
0017ba31
YW
87 /* If not, then select one from static addresses. */
88 SET_FOREACH(address, link->static_addresses)
89 if (address->family == AF_INET &&
90 !in4_addr_is_localhost(&address->in_addr.in) &&
91 in4_addr_is_null(&address->in_addr_peer.in)) {
92 *ret = address;
93 return 0;
94 }
8fcf1d61 95
0017ba31 96 return -ENOENT;
8fcf1d61
YW
97}
98
2a71d57f
LP
99static int link_push_uplink_to_dhcp_server(
100 Link *link,
2324fd3a 101 sd_dhcp_lease_server_type_t what,
2a71d57f
LP
102 sd_dhcp_server *s) {
103
8fcf1d61 104 _cleanup_free_ struct in_addr *addresses = NULL;
2a71d57f 105 bool use_dhcp_lease_data = true;
319a4f4b 106 size_t n_addresses = 0;
8fcf1d61 107
2a71d57f 108 assert(link);
8fcf1d61 109
2a71d57f
LP
110 if (!link->network)
111 return 0;
112 assert(link->network);
8fcf1d61 113
2a71d57f 114 log_link_debug(link, "Copying %s from link", dhcp_lease_server_type_to_string(what));
8fcf1d61 115
2a71d57f 116 switch (what) {
8fcf1d61 117
2a71d57f
LP
118 case SD_DHCP_LEASE_DNS:
119 /* For DNS we have a special case. We the data configured explicitly locally along with the
120 * data from the DHCP lease. */
8fcf1d61 121
2a71d57f
LP
122 for (unsigned i = 0; i < link->network->n_dns; i++) {
123 struct in_addr ia;
8fcf1d61 124
2a71d57f 125 /* Only look for IPv4 addresses */
e77bd3fd 126 if (link->network->dns[i]->family != AF_INET)
2a71d57f 127 continue;
8fcf1d61 128
e77bd3fd 129 ia = link->network->dns[i]->address.in;
2a71d57f
LP
130
131 /* Never propagate obviously borked data */
132 if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
133 continue;
134
319a4f4b 135 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
8fcf1d61
YW
136 return log_oom();
137
2a71d57f 138 addresses[n_addresses++] = ia;
8fcf1d61 139 }
8fcf1d61 140
2a71d57f
LP
141 use_dhcp_lease_data = link->network->dhcp_use_dns;
142 break;
8fcf1d61 143
2a71d57f
LP
144 case SD_DHCP_LEASE_NTP: {
145 char **i;
8fcf1d61 146
2a71d57f
LP
147 /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot
148 * propagate via DHCP. Hence let's only propagate those which are IP addresses. */
284e8fd0 149
2a71d57f
LP
150 STRV_FOREACH(i, link->network->ntp) {
151 union in_addr_union ia;
284e8fd0 152
2a71d57f
LP
153 if (in_addr_from_string(AF_INET, *i, &ia) < 0)
154 continue;
284e8fd0 155
2a71d57f
LP
156 /* Never propagate obviously borked data */
157 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
158 continue;
284e8fd0 159
319a4f4b 160 if (!GREEDY_REALLOC(addresses, n_addresses + 1))
2a71d57f 161 return log_oom();
284e8fd0 162
2a71d57f
LP
163 addresses[n_addresses++] = ia.in;
164 }
284e8fd0 165
2a71d57f 166 use_dhcp_lease_data = link->network->dhcp_use_ntp;
24e6f458 167 break;
2a71d57f 168 }
284e8fd0 169
ddb82ec2 170 case SD_DHCP_LEASE_SIP:
2a71d57f
LP
171
172 /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */
173 use_dhcp_lease_data = link->network->dhcp_use_sip;
24e6f458 174 break;
284e8fd0 175
2a71d57f
LP
176 case SD_DHCP_LEASE_POP3:
177 case SD_DHCP_LEASE_SMTP:
ddb82ec2 178 case SD_DHCP_LEASE_LPR:
2a71d57f
LP
179 /* For the other server types we currently do not allow local configuration of server data,
180 * since there are typically no local consumers of the data. */
c4e585a3 181 break;
d361b373 182
24e6f458 183 default:
2a71d57f 184 assert_not_reached("Unexpected server type");
f6269fe7
SS
185 }
186
2a71d57f 187 if (use_dhcp_lease_data && link->dhcp_lease) {
24e6f458 188 const struct in_addr *da;
f6269fe7 189
a2706075 190 int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
f6269fe7 191 if (n > 0) {
319a4f4b 192 if (!GREEDY_REALLOC(addresses, n_addresses + n))
f6269fe7
SS
193 return log_oom();
194
2a71d57f
LP
195 for (int j = 0; j < n; j++)
196 if (in4_addr_is_non_local(&da[j]))
197 addresses[n_addresses++] = da[j];
f6269fe7
SS
198 }
199 }
200
201 if (n_addresses <= 0)
202 return 0;
203
24e6f458 204 return sd_dhcp_server_set_servers(s, what, addresses, n_addresses);
299d578f
SS
205}
206
319a4f4b
LP
207static int dhcp4_server_parse_dns_server_string_and_warn(
208 const char *string,
209 struct in_addr **addresses,
210 size_t *n_addresses) {
211
dd1d3060
MAL
212 for (;;) {
213 _cleanup_free_ char *word = NULL, *server_name = NULL;
214 union in_addr_union address;
215 int family, r, ifindex = 0;
216
217 r = extract_first_word(&string, &word, NULL, 0);
218 if (r < 0)
219 return r;
220 if (r == 0)
221 break;
222
223 r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
224 if (r < 0) {
225 log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word);
226 continue;
227 }
228
229 /* Only look for IPv4 addresses */
230 if (family != AF_INET)
231 continue;
232
233 /* Never propagate obviously borked data */
234 if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in))
235 continue;
236
319a4f4b 237 if (!GREEDY_REALLOC(*addresses, *n_addresses + 1))
dd1d3060
MAL
238 return log_oom();
239
240 (*addresses)[(*n_addresses)++] = address.in;
241 }
242
243 return 0;
244}
245
246static int dhcp4_server_set_dns_from_resolve_conf(Link *link) {
247 _cleanup_free_ struct in_addr *addresses = NULL;
dd1d3060 248 _cleanup_fclose_ FILE *f = NULL;
319a4f4b 249 size_t n_addresses = 0;
dd1d3060
MAL
250 int n = 0, r;
251
252 f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re");
253 if (!f) {
254 if (errno == ENOENT)
255 return 0;
256
257 return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m");
258 }
259
260 for (;;) {
261 _cleanup_free_ char *line = NULL;
262 const char *a;
263 char *l;
264
265 r = read_line(f, LONG_LINE_MAX, &line);
266 if (r < 0)
267 return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m");
268 if (r == 0)
269 break;
270
271 n++;
272
273 l = strstrip(line);
274 if (IN_SET(*l, '#', ';', 0))
275 continue;
276
277 a = first_word(l, "nameserver");
278 if (!a)
279 continue;
280
319a4f4b 281 r = dhcp4_server_parse_dns_server_string_and_warn(a, &addresses, &n_addresses);
dd1d3060
MAL
282 if (r < 0)
283 log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
284 }
285
286 if (n_addresses <= 0)
287 return 0;
288
289 return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses);
290}
291
8fcf1d61 292int dhcp4_server_configure(Link *link) {
8fcf1d61 293 bool acquired_uplink = false;
461dbb2f 294 sd_dhcp_option *p;
c517a49b 295 DHCPStaticLease *static_lease;
564ca984
SS
296 Link *uplink = NULL;
297 Address *address;
11c38d3e 298 bool bind_to_interface;
8fcf1d61
YW
299 int r;
300
5ae0fb7f
YW
301 assert(link);
302
303 if (!link_dhcp4_server_enabled(link))
304 return 0;
305
306 if (!(link->flags & IFF_UP))
307 return 0;
308
309 if (!link->dhcp_server) {
310 r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex);
311 if (r < 0)
312 return r;
313
314 r = sd_dhcp_server_attach_event(link->dhcp_server, link->manager->event, 0);
315 if (r < 0)
316 return r;
317 }
318
a95e9306
LK
319 r = sd_dhcp_server_set_callback(link->dhcp_server, dhcp_server_callback, link);
320 if (r < 0)
321 return log_link_warning_errno(link, r, "Failed to set callback for DHCPv4 server instance: %m");
322
0017ba31
YW
323 r = link_find_dhcp_server_address(link, &address);
324 if (r < 0)
325 return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m");
8fcf1d61
YW
326
327 /* use the server address' subnet as the pool */
328 r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
329 link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
330 if (r < 0)
c00c3b64 331 return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
8fcf1d61
YW
332
333 /* TODO:
334 r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
335 if (r < 0)
336 return r;
337 */
338
339 if (link->network->dhcp_server_max_lease_time_usec > 0) {
340 r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
341 DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
342 if (r < 0)
c00c3b64 343 return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
8fcf1d61
YW
344 }
345
346 if (link->network->dhcp_server_default_lease_time_usec > 0) {
347 r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
348 DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
349 if (r < 0)
c00c3b64 350 return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
8fcf1d61
YW
351 }
352
2324fd3a 353 for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) {
2a71d57f
LP
354
355 if (!link->network->dhcp_server_emit[type].emit)
356 continue;
357
358 if (link->network->dhcp_server_emit[type].n_addresses > 0)
359 /* Explicitly specified servers to emit */
360 r = sd_dhcp_server_set_servers(
361 link->dhcp_server,
362 type,
363 link->network->dhcp_server_emit[type].addresses,
364 link->network->dhcp_server_emit[type].n_addresses);
365 else {
366 /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */
367 if (!acquired_uplink) {
368 uplink = manager_find_uplink(link->manager, link);
369 acquired_uplink = true;
370 }
371
372 if (uplink && uplink->network)
373 r = link_push_uplink_to_dhcp_server(uplink, type, link->dhcp_server);
374 else if (type == SD_DHCP_LEASE_DNS)
375 r = dhcp4_server_set_dns_from_resolve_conf(link);
24e6f458 376 else {
2a71d57f
LP
377 log_link_debug(link,
378 "Not emitting %s on link, couldn't find suitable uplink.",
379 dhcp_lease_server_type_to_string(type));
380 continue;
24e6f458 381 }
299d578f 382 }
284e8fd0 383
2a71d57f
LP
384 if (r < 0)
385 log_link_warning_errno(link, r,
386 "Failed to set %s for DHCP server, ignoring: %m",
387 dhcp_lease_server_type_to_string(type));
388 }
389
8fcf1d61
YW
390 r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
391 if (r < 0)
a0fa3ef7 392 return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
8fcf1d61 393
c95df587
YA
394 r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target);
395 if (r < 0)
396 return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m");
397
11c38d3e
YA
398 bind_to_interface = sd_dhcp_server_is_in_relay_mode(link->dhcp_server) ? false : link->network->dhcp_server_bind_to_interface;
399 r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, bind_to_interface);
400 if (r < 0)
401 return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m");
402
403 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);
404 if (r < 0)
405 return log_link_error_errno(link, r, "Failed to set agent circuit/remote id for DHCP server: %m");
406
8fcf1d61
YW
407 if (link->network->dhcp_server_emit_timezone) {
408 _cleanup_free_ char *buffer = NULL;
7b5018ca 409 const char *tz = NULL;
8fcf1d61
YW
410
411 if (link->network->dhcp_server_timezone)
412 tz = link->network->dhcp_server_timezone;
413 else {
414 r = get_timezone(&buffer);
415 if (r < 0)
7b5018ca 416 log_link_warning_errno(link, r, "Failed to determine timezone, not sending timezone: %m");
417 else
418 tz = buffer;
8fcf1d61
YW
419 }
420
7b5018ca 421 if (tz) {
422 r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
423 if (r < 0)
424 return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m");
425 }
8fcf1d61 426 }
564ca984 427
90e74a66 428 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) {
461dbb2f 429 r = sd_dhcp_server_add_option(link->dhcp_server, p);
564ca984
SS
430 if (r == -EEXIST)
431 continue;
432 if (r < 0)
c00c3b64 433 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
564ca984
SS
434 }
435
90e74a66 436 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options) {
7354900d
DW
437 r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p);
438 if (r == -EEXIST)
439 continue;
440 if (r < 0)
441 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
442 }
443
c517a49b 444 HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) {
445 r = sd_dhcp_server_set_static_lease(link->dhcp_server, &static_lease->address, static_lease->client_id, static_lease->client_id_size);
446 if (r < 0)
447 return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
448 }
449
8fcf1d61
YW
450 if (!sd_dhcp_server_is_running(link->dhcp_server)) {
451 r = sd_dhcp_server_start(link->dhcp_server);
452 if (r < 0)
a0fa3ef7 453 return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
5ae0fb7f
YW
454
455 log_link_debug(link, "Offering DHCPv4 leases");
8fcf1d61
YW
456 }
457
458 return 0;
459}
460
11c38d3e
YA
461int config_parse_dhcp_server_relay_agent_suboption(
462 const char *unit,
463 const char *filename,
464 unsigned line,
465 const char *section,
466 unsigned section_line,
467 const char *lvalue,
468 int ltype,
469 const char *rvalue,
470 void *data,
471 void *userdata) {
472
473 char **suboption_value = data;
474 char* p;
475
476 assert(filename);
477 assert(lvalue);
478 assert(rvalue);
479
11c38d3e
YA
480 if (isempty(rvalue)) {
481 *suboption_value = mfree(*suboption_value);
482 return 0;
483 }
484
485 p = startswith(rvalue, "string:");
486 if (!p) {
487 log_syntax(unit, LOG_WARNING, filename, line, 0,
488 "Failed to parse %s=%s'. Invalid format, ignoring.", lvalue, rvalue);
489 return 0;
490 }
491 return free_and_strdup(suboption_value, empty_to_null(p));
492}
493
2a71d57f 494int config_parse_dhcp_server_emit(
8fcf1d61
YW
495 const char *unit,
496 const char *filename,
497 unsigned line,
2a71d57f
LP
498 const char *section,
499 unsigned section_line,
8fcf1d61 500 const char *lvalue,
2a71d57f 501 int ltype,
8fcf1d61 502 const char *rvalue,
2a71d57f
LP
503 void *data,
504 void *userdata) {
8fcf1d61 505
2a71d57f
LP
506 NetworkDHCPServerEmitAddress *emit = data;
507
508 assert(emit);
8fcf1d61
YW
509 assert(rvalue);
510
c1997a5b 511 for (const char *p = rvalue;;) {
8fcf1d61
YW
512 _cleanup_free_ char *w = NULL;
513 union in_addr_union a;
c1997a5b 514 int r;
8fcf1d61
YW
515
516 r = extract_first_word(&p, &w, NULL, 0);
517 if (r == -ENOMEM)
518 return log_oom();
519 if (r < 0) {
d96edb2c 520 log_syntax(unit, LOG_WARNING, filename, line, r,
8fcf1d61
YW
521 "Failed to extract word, ignoring: %s", rvalue);
522 return 0;
523 }
524 if (r == 0)
c1997a5b 525 return 0;
8fcf1d61
YW
526
527 r = in_addr_from_string(AF_INET, w, &a);
528 if (r < 0) {
d96edb2c 529 log_syntax(unit, LOG_WARNING, filename, line, r,
c1997a5b 530 "Failed to parse %s= address '%s', ignoring: %m", lvalue, w);
8fcf1d61
YW
531 continue;
532 }
533
2a71d57f 534 struct in_addr *m = reallocarray(emit->addresses, emit->n_addresses + 1, sizeof(struct in_addr));
8fcf1d61
YW
535 if (!m)
536 return log_oom();
537
2a71d57f
LP
538 emit->addresses = m;
539 emit->addresses[emit->n_addresses++] = a.in;
8fcf1d61 540 }
8fcf1d61 541}
0017ba31
YW
542
543int config_parse_dhcp_server_address(
544 const char *unit,
545 const char *filename,
546 unsigned line,
547 const char *section,
548 unsigned section_line,
549 const char *lvalue,
550 int ltype,
551 const char *rvalue,
552 void *data,
553 void *userdata) {
554
555 Network *network = userdata;
556 union in_addr_union a;
557 unsigned char prefixlen;
558 int r;
559
560 assert(filename);
561 assert(lvalue);
562 assert(rvalue);
563
564 if (isempty(rvalue)) {
565 network->dhcp_server_address = (struct in_addr) {};
566 network->dhcp_server_address_prefixlen = 0;
567 return 0;
568 }
569
570 r = in_addr_prefix_from_string(rvalue, AF_INET, &a, &prefixlen);
571 if (r < 0) {
572 log_syntax(unit, LOG_WARNING, filename, line, r,
573 "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
574 return 0;
575 }
576 if (in4_addr_is_null(&a.in) || in4_addr_is_localhost(&a.in)) {
577 log_syntax(unit, LOG_WARNING, filename, line, 0,
578 "DHCP server address cannot be the ANY address or a localhost address, "
579 "ignoring assignment: %s", rvalue);
580 return 0;
581 }
582
583 network->dhcp_server_address = a.in;
584 network->dhcp_server_address_prefixlen = prefixlen;
585 return 0;
586}