]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-common.c
Merge pull request #15840 from Werkov/mkosi-opensuse
[thirdparty/systemd.git] / src / network / networkd-dhcp-common.c
CommitLineData
ca5ad760
YW
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
0e96961d 3#include "dhcp-internal.h"
e7d5fe17 4#include "dhcp6-internal.h"
0e96961d 5#include "escape.h"
2805536b 6#include "in-addr-util.h"
ca5ad760
YW
7#include "networkd-dhcp-common.h"
8#include "networkd-network.h"
9#include "parse-util.h"
10#include "string-table.h"
426588bc 11#include "strv.h"
3175a8c2 12#include "web-util.h"
ca5ad760
YW
13
14int config_parse_dhcp(
15 const char* unit,
16 const char *filename,
17 unsigned line,
18 const char *section,
19 unsigned section_line,
20 const char *lvalue,
21 int ltype,
22 const char *rvalue,
23 void *data,
24 void *userdata) {
25
2d792895 26 AddressFamily *dhcp = data, s;
ca5ad760
YW
27
28 assert(filename);
29 assert(lvalue);
30 assert(rvalue);
31 assert(data);
32
33 /* Note that this is mostly like
2d792895 34 * config_parse_address_family(), except that it
ca5ad760
YW
35 * understands some old names for the enum values */
36
2d792895 37 s = address_family_from_string(rvalue);
ca5ad760
YW
38 if (s < 0) {
39
40 /* Previously, we had a slightly different enum here,
41 * support its values for compatibility. */
42
43 if (streq(rvalue, "none"))
44 s = ADDRESS_FAMILY_NO;
45 else if (streq(rvalue, "v4"))
46 s = ADDRESS_FAMILY_IPV4;
47 else if (streq(rvalue, "v6"))
48 s = ADDRESS_FAMILY_IPV6;
49 else if (streq(rvalue, "both"))
50 s = ADDRESS_FAMILY_YES;
51 else {
52 log_syntax(unit, LOG_ERR, filename, line, 0,
53 "Failed to parse DHCP option, ignoring: %s", rvalue);
54 return 0;
55 }
56
57 log_syntax(unit, LOG_WARNING, filename, line, 0,
58 "DHCP=%s is deprecated, please use DHCP=%s instead.",
2d792895 59 rvalue, address_family_to_string(s));
ca5ad760
YW
60 }
61
62 *dhcp = s;
63 return 0;
64}
65
4f7331a8
YW
66int config_parse_dhcp_use_dns(
67 const char* unit,
68 const char *filename,
69 unsigned line,
70 const char *section,
71 unsigned section_line,
72 const char *lvalue,
73 int ltype,
74 const char *rvalue,
75 void *data,
76 void *userdata) {
77
78 Network *network = data;
79 int r;
80
81 assert(filename);
82 assert(lvalue);
83 assert(rvalue);
84 assert(data);
85
86 r = parse_boolean(rvalue);
87 if (r < 0) {
88 log_syntax(unit, LOG_ERR, filename, line, r,
89 "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
90 return 0;
91 }
92
93 network->dhcp_use_dns = r;
94 network->dhcp6_use_dns = r;
95
96 return 0;
97}
98
299d578f
SS
99int config_parse_dhcp_use_sip(
100 const char* unit,
101 const char *filename,
102 unsigned line,
103 const char *section,
104 unsigned section_line,
105 const char *lvalue,
106 int ltype,
107 const char *rvalue,
108 void *data,
109 void *userdata) {
110
111 Network *network = data;
112 int r;
113
114 assert(filename);
115 assert(lvalue);
116 assert(rvalue);
117 assert(data);
118
119 r = parse_boolean(rvalue);
120 if (r < 0) {
121 log_syntax(unit, LOG_ERR, filename, line, r,
122 "Failed to parse UseSIP=%s, ignoring assignment: %m", rvalue);
123 return 0;
124 }
125
126 network->dhcp_use_sip = r;
127
128 return 0;
129}
130
4f7331a8
YW
131int config_parse_dhcp_use_ntp(
132 const char* unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
136 unsigned section_line,
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *data,
141 void *userdata) {
142
143 Network *network = data;
144 int r;
145
146 assert(filename);
147 assert(lvalue);
148 assert(rvalue);
149 assert(data);
150
151 r = parse_boolean(rvalue);
152 if (r < 0) {
153 log_syntax(unit, LOG_ERR, filename, line, r,
154 "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
155 return 0;
156 }
157
158 network->dhcp_use_ntp = r;
159 network->dhcp6_use_ntp = r;
160
161 return 0;
162}
163
ca5ad760
YW
164int config_parse_section_route_table(
165 const char *unit,
166 const char *filename,
167 unsigned line,
168 const char *section,
169 unsigned section_line,
170 const char *lvalue,
171 int ltype,
172 const char *rvalue,
173 void *data,
174 void *userdata) {
175
176 Network *network = data;
177 uint32_t rt;
178 int r;
179
180 assert(filename);
181 assert(lvalue);
182 assert(rvalue);
183 assert(data);
184
185 r = safe_atou32(rvalue, &rt);
186 if (r < 0) {
187 log_syntax(unit, LOG_ERR, filename, line, r,
188 "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
189 return 0;
190 }
191
426588bc 192 if (STRPTR_IN_SET(section, "DHCP", "DHCPv4")) {
ca5ad760
YW
193 network->dhcp_route_table = rt;
194 network->dhcp_route_table_set = true;
195 } else { /* section is IPv6AcceptRA */
196 network->ipv6_accept_ra_route_table = rt;
197 network->ipv6_accept_ra_route_table_set = true;
198 }
199
200 return 0;
201}
202
203int config_parse_iaid(const char *unit,
204 const char *filename,
205 unsigned line,
206 const char *section,
207 unsigned section_line,
208 const char *lvalue,
209 int ltype,
210 const char *rvalue,
211 void *data,
212 void *userdata) {
213 Network *network = data;
214 uint32_t iaid;
215 int r;
216
217 assert(filename);
218 assert(lvalue);
219 assert(rvalue);
220 assert(network);
221
222 r = safe_atou32(rvalue, &iaid);
223 if (r < 0) {
224 log_syntax(unit, LOG_ERR, filename, line, r,
225 "Unable to read IAID, ignoring assignment: %s", rvalue);
226 return 0;
227 }
228
229 network->iaid = iaid;
230 network->iaid_set = true;
231
232 return 0;
233}
234
2805536b
SS
235int config_parse_dhcp6_pd_hint(
236 const char* unit,
237 const char *filename,
238 unsigned line,
239 const char *section,
240 unsigned section_line,
241 const char *lvalue,
242 int ltype,
243 const char *rvalue,
244 void *data,
245 void *userdata) {
246
247 Network *network = data;
248 int r;
249
250 assert(filename);
251 assert(lvalue);
252 assert(rvalue);
253 assert(data);
254
255 r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
256 if (r < 0) {
257 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
258 return 0;
259 }
260
261 if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
262 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
263 network->dhcp6_pd_length = 0;
264 return 0;
265 }
266
267 return 0;
268}
269
f37f2a6b 270int config_parse_dhcp_user_class(
3175a8c2
SS
271 const char *unit,
272 const char *filename,
273 unsigned line,
274 const char *section,
275 unsigned section_line,
276 const char *lvalue,
277 int ltype,
278 const char *rvalue,
279 void *data,
280 void *userdata) {
281
f37f2a6b
SS
282 char ***l = data;
283 int r;
284
285 assert(l);
286 assert(lvalue);
287 assert(rvalue);
288
289 if (isempty(rvalue)) {
290 *l = strv_free(*l);
291 return 0;
292 }
293
294 for (;;) {
295 _cleanup_free_ char *w = NULL;
296
297 r = extract_first_word(&rvalue, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
298 if (r == -ENOMEM)
299 return log_oom();
300 if (r < 0) {
301 log_syntax(unit, LOG_ERR, filename, line, r,
302 "Failed to split user classes option, ignoring: %s", rvalue);
303 break;
304 }
305 if (r == 0)
306 break;
307
308 if (ltype == AF_INET) {
309 if (strlen(w) > UINT8_MAX) {
310 log_syntax(unit, LOG_ERR, filename, line, 0,
311 "%s length is not in the range 1-255, ignoring.", w);
312 continue;
313 }
314 } else {
315 if (strlen(w) > UINT16_MAX) {
316 log_syntax(unit, LOG_ERR, filename, line, 0,
317 "%s length is not in the range 1-65535, ignoring.", w);
318 continue;
319 }
320 }
321
322 r = strv_push(l, w);
323 if (r < 0)
324 return log_oom();
325
326 w = NULL;
327 }
328
329 return 0;
330}
331
ed0d1b2e
SS
332int config_parse_dhcp_vendor_class(
333 const char *unit,
334 const char *filename,
335 unsigned line,
336 const char *section,
337 unsigned section_line,
338 const char *lvalue,
339 int ltype,
340 const char *rvalue,
341 void *data,
342 void *userdata) {
343 char ***l = data;
344 int r;
345
346 assert(l);
347 assert(lvalue);
348 assert(rvalue);
349
350 if (isempty(rvalue)) {
351 *l = strv_free(*l);
352 return 0;
353 }
354
355 for (;;) {
356 _cleanup_free_ char *w = NULL;
357
358 r = extract_first_word(&rvalue, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
359 if (r == -ENOMEM)
360 return log_oom();
361 if (r < 0) {
362 log_syntax(unit, LOG_ERR, filename, line, r,
363 "Failed to split vendor classes option, ignoring: %s", rvalue);
364 break;
365 }
366 if (r == 0)
367 break;
368
369 if (strlen(w) > UINT8_MAX) {
370 log_syntax(unit, LOG_ERR, filename, line, 0,
371 "%s length is not in the range 1-255, ignoring.", w);
372 continue;
373 }
374
375 r = strv_push(l, w);
376 if (r < 0)
377 return log_oom();
378
379 w = NULL;
380 }
381
382 return 0;
383}
384
f37f2a6b
SS
385int config_parse_dhcp6_mud_url(
386 const char *unit,
387 const char *filename,
388 unsigned line,
389 const char *section,
390 unsigned section_line,
391 const char *lvalue,
392 int ltype,
393 const char *rvalue,
394 void *data,
395 void *userdata) {
3175a8c2
SS
396 _cleanup_free_ char *unescaped = NULL;
397 Network *network = data;
398 int r;
399
400 assert(filename);
401 assert(lvalue);
402 assert(rvalue);
403
404 if (isempty(rvalue)) {
405 network->dhcp6_mudurl = mfree(network->dhcp6_mudurl);
406 return 0;
407 }
408
409 r = cunescape(rvalue, 0, &unescaped);
410 if (r < 0) {
411 log_syntax(unit, LOG_ERR, filename, line, r,
412 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
413 return 0;
414 }
415
ffed0205 416 if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) {
3175a8c2
SS
417 log_syntax(unit, LOG_ERR, filename, line, 0,
418 "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
419
420 return 0;
421 }
422
423 return free_and_replace(network->dhcp6_mudurl, unescaped);
424}
425
0e96961d
YW
426int config_parse_dhcp_send_option(
427 const char *unit,
428 const char *filename,
429 unsigned line,
430 const char *section,
431 unsigned section_line,
432 const char *lvalue,
433 int ltype,
434 const char *rvalue,
435 void *data,
436 void *userdata) {
437
e7d5fe17
AD
438 _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL;
439 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL;
0e96961d 440 _cleanup_free_ char *word = NULL, *q = NULL;
83b56c70 441 OrderedHashmap **options = data;
0e96961d
YW
442 union in_addr_union addr;
443 DHCPOptionDataType type;
e7d5fe17
AD
444 uint8_t u8, uint8_data;
445 uint16_t u16, uint16_data;
0e96961d
YW
446 uint32_t uint32_data;
447 const void *udata;
448 const char *p;
449 ssize_t sz;
450 int r;
451
452 assert(filename);
453 assert(lvalue);
454 assert(rvalue);
455 assert(data);
456
457 if (isempty(rvalue)) {
458 *options = ordered_hashmap_free(*options);
459 return 0;
460 }
461
462 p = rvalue;
463 r = extract_first_word(&p, &word, ":", 0);
464 if (r == -ENOMEM)
465 return log_oom();
466 if (r <= 0) {
467 log_syntax(unit, LOG_ERR, filename, line, r,
468 "Invalid DHCP option, ignoring assignment: %s", rvalue);
469 return 0;
470 }
471
e7d5fe17
AD
472 if (ltype == AF_INET6) {
473 r = safe_atou16(word, &u16);
474 if (r < 0) {
475 log_syntax(unit, LOG_ERR, filename, line, r,
476 "Invalid DHCP option, ignoring assignment: %s", rvalue);
477 return 0;
478 }
f37f2a6b 479 if (u16 < 1 || u16 >= UINT16_MAX) {
e7d5fe17
AD
480 log_syntax(unit, LOG_ERR, filename, line, 0,
481 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
482 return 0;
483 }
484 } else {
485 r = safe_atou8(word, &u8);
486 if (r < 0) {
487 log_syntax(unit, LOG_ERR, filename, line, r,
488 "Invalid DHCP option, ignoring assignment: %s", rvalue);
489 return 0;
490 }
ffed0205 491 if (u8 < 1 || u8 >= UINT8_MAX) {
e7d5fe17
AD
492 log_syntax(unit, LOG_ERR, filename, line, 0,
493 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
494 return 0;
495 }
0e96961d
YW
496 }
497
3db7d5d2 498 word = mfree(word);
0e96961d
YW
499 r = extract_first_word(&p, &word, ":", 0);
500 if (r == -ENOMEM)
501 return log_oom();
1eb73422 502 if (r <= 0 || isempty(p)) {
0e96961d
YW
503 log_syntax(unit, LOG_ERR, filename, line, r,
504 "Invalid DHCP option, ignoring assignment: %s", rvalue);
505 return 0;
506 }
507
508 type = dhcp_option_data_type_from_string(word);
509 if (type < 0) {
510 log_syntax(unit, LOG_ERR, filename, line, 0,
511 "Invalid DHCP option data type, ignoring assignment: %s", p);
512 return 0;
513 }
514
515 switch(type) {
516 case DHCP_OPTION_DATA_UINT8:{
517 r = safe_atou8(p, &uint8_data);
518 if (r < 0) {
519 log_syntax(unit, LOG_ERR, filename, line, r,
e7d5fe17 520 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
0e96961d
YW
521 return 0;
522 }
523
524 udata = &uint8_data;
525 sz = sizeof(uint8_t);
526 break;
527 }
528 case DHCP_OPTION_DATA_UINT16:{
529 r = safe_atou16(p, &uint16_data);
530 if (r < 0) {
531 log_syntax(unit, LOG_ERR, filename, line, r,
e7d5fe17 532 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
0e96961d
YW
533 return 0;
534 }
535
536 udata = &uint16_data;
537 sz = sizeof(uint16_t);
538 break;
539 }
540 case DHCP_OPTION_DATA_UINT32: {
541 r = safe_atou32(p, &uint32_data);
542 if (r < 0) {
543 log_syntax(unit, LOG_ERR, filename, line, r,
e7d5fe17 544 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
0e96961d
YW
545 return 0;
546 }
547
548 udata = &uint32_data;
549 sz = sizeof(uint32_t);
550
551 break;
552 }
553 case DHCP_OPTION_DATA_IPV4ADDRESS: {
554 r = in_addr_from_string(AF_INET, p, &addr);
555 if (r < 0) {
556 log_syntax(unit, LOG_ERR, filename, line, r,
e7d5fe17 557 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
0e96961d
YW
558 return 0;
559 }
560
561 udata = &addr.in;
562 sz = sizeof(addr.in.s_addr);
563 break;
564 }
e7d5fe17
AD
565 case DHCP_OPTION_DATA_IPV6ADDRESS: {
566 r = in_addr_from_string(AF_INET6, p, &addr);
567 if (r < 0) {
568 log_syntax(unit, LOG_ERR, filename, line, r,
569 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
570 return 0;
571 }
572
573 udata = &addr.in6;
574 sz = sizeof(addr.in6.s6_addr);
575 break;
576 }
0e96961d 577 case DHCP_OPTION_DATA_STRING:
732e3a61 578 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
0e96961d
YW
579 if (sz < 0) {
580 log_syntax(unit, LOG_ERR, filename, line, sz,
e7d5fe17 581 "Failed to decode DHCP option data, ignoring assignment: %s", p);
0e96961d
YW
582 }
583
584 udata = q;
585 break;
586 default:
587 return -EINVAL;
588 }
589
e7d5fe17
AD
590 if (ltype == AF_INET6) {
591 r = sd_dhcp6_option_new(u16, udata, sz, &opt6);
592 if (r < 0) {
593 log_syntax(unit, LOG_ERR, filename, line, r,
594 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
595 return 0;
596 }
0e96961d 597
e7d5fe17
AD
598 r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
599 if (r < 0)
600 return log_oom();
0e96961d 601
e7d5fe17
AD
602 /* Overwrite existing option */
603 old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
604 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
605 if (r < 0) {
606 log_syntax(unit, LOG_ERR, filename, line, r,
607 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
608 return 0;
609 }
610 TAKE_PTR(opt6);
611 } else {
612 r = sd_dhcp_option_new(u8, udata, sz, &opt4);
613 if (r < 0) {
614 log_syntax(unit, LOG_ERR, filename, line, r,
615 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
616 return 0;
617 }
0e96961d 618
e7d5fe17
AD
619 r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
620 if (r < 0)
621 return log_oom();
622
623 /* Overwrite existing option */
624 old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
625 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
626 if (r < 0) {
627 log_syntax(unit, LOG_ERR, filename, line, r,
628 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
629 return 0;
630 }
631 TAKE_PTR(opt4);
632 }
0e96961d
YW
633 return 0;
634}
635
35f6a5cb
SS
636int config_parse_dhcp_request_options(
637 const char *unit,
638 const char *filename,
639 unsigned line,
640 const char *section,
641 unsigned section_line,
642 const char *lvalue,
643 int ltype,
644 const char *rvalue,
645 void *data,
646 void *userdata) {
647
648 Network *network = data;
649 const char *p;
650 int r;
651
652 assert(filename);
653 assert(lvalue);
654 assert(rvalue);
655 assert(data);
656
657 if (isempty(rvalue)) {
658 if (ltype == AF_INET)
659 network->dhcp_request_options = set_free(network->dhcp_request_options);
660 else
661 network->dhcp6_request_options = set_free(network->dhcp6_request_options);
662
663 return 0;
664 }
665
666 for (p = rvalue;;) {
667 _cleanup_free_ char *n = NULL;
668 uint32_t i;
669
670 r = extract_first_word(&p, &n, NULL, 0);
671 if (r < 0) {
672 log_syntax(unit, LOG_ERR, filename, line, r,
673 "Failed to parse DHCP request option, ignoring assignment: %s",
674 rvalue);
675 return 0;
676 }
677 if (r == 0)
678 return 0;
679
680 r = safe_atou32(n, &i);
681 if (r < 0) {
682 log_syntax(unit, LOG_ERR, filename, line, r,
683 "DHCP request option is invalid, ignoring assignment: %s", n);
684 continue;
685 }
686
ffed0205 687 if (i < 1 || i >= UINT8_MAX) {
35f6a5cb
SS
688 log_syntax(unit, LOG_ERR, filename, line, r,
689 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
690 continue;
691 }
692
693 if (ltype == AF_INET)
694 r = set_ensure_allocated(&network->dhcp_request_options, NULL);
695 else
696 r = set_ensure_allocated(&network->dhcp6_request_options, NULL);
697 if (r < 0)
698 return log_oom();
699
700 if (ltype == AF_INET)
701 r = set_put(network->dhcp_request_options, UINT32_TO_PTR(i));
702 else
703 r = set_put(network->dhcp6_request_options, UINT32_TO_PTR(i));
704 if (r < 0)
705 log_syntax(unit, LOG_ERR, filename, line, r,
706 "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
707 }
708
709 return 0;
710}
711
ca5ad760
YW
712DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
713 "Failed to parse DHCP use domains setting");
714
715static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
716 [DHCP_USE_DOMAINS_NO] = "no",
717 [DHCP_USE_DOMAINS_ROUTE] = "route",
718 [DHCP_USE_DOMAINS_YES] = "yes",
719};
720
721DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
2e5580a8
YW
722
723static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
724 [DHCP_OPTION_DATA_UINT8] = "uint8",
725 [DHCP_OPTION_DATA_UINT16] = "uint16",
726 [DHCP_OPTION_DATA_UINT32] = "uint32",
727 [DHCP_OPTION_DATA_STRING] = "string",
728 [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
e7d5fe17 729 [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
2e5580a8
YW
730};
731
732DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);