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