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