]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp-common.c
network: DHCPv6 - Add support set arbitary request options
[thirdparty/systemd.git] / src / network / networkd-dhcp-common.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "dhcp-internal.h"
4 #include "dhcp6-internal.h"
5 #include "escape.h"
6 #include "in-addr-util.h"
7 #include "networkd-dhcp-common.h"
8 #include "networkd-network.h"
9 #include "parse-util.h"
10 #include "string-table.h"
11 #include "strv.h"
12 #include "web-util.h"
13
14 int 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
26 AddressFamily *dhcp = data, s;
27
28 assert(filename);
29 assert(lvalue);
30 assert(rvalue);
31 assert(data);
32
33 /* Note that this is mostly like
34 * config_parse_address_family(), except that it
35 * understands some old names for the enum values */
36
37 s = address_family_from_string(rvalue);
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.",
59 rvalue, address_family_to_string(s));
60 }
61
62 *dhcp = s;
63 return 0;
64 }
65
66 int 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
99 int 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
131 int 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
164 int 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
192 if (STRPTR_IN_SET(section, "DHCP", "DHCPv4")) {
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
203 int 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
235 int 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
270 int config_parse_dhcp6_mud_url(
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
282 _cleanup_free_ char *unescaped = NULL;
283 Network *network = data;
284 int r;
285
286 assert(filename);
287 assert(lvalue);
288 assert(rvalue);
289
290 if (isempty(rvalue)) {
291 network->dhcp6_mudurl = mfree(network->dhcp6_mudurl);
292 return 0;
293 }
294
295 r = cunescape(rvalue, 0, &unescaped);
296 if (r < 0) {
297 log_syntax(unit, LOG_ERR, filename, line, r,
298 "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
299 return 0;
300 }
301
302 if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
303 log_syntax(unit, LOG_ERR, filename, line, 0,
304 "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
305
306 return 0;
307 }
308
309 return free_and_replace(network->dhcp6_mudurl, unescaped);
310 }
311
312 int config_parse_dhcp_send_option(
313 const char *unit,
314 const char *filename,
315 unsigned line,
316 const char *section,
317 unsigned section_line,
318 const char *lvalue,
319 int ltype,
320 const char *rvalue,
321 void *data,
322 void *userdata) {
323
324 _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL;
325 _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL;
326 _cleanup_free_ char *word = NULL, *q = NULL;
327 OrderedHashmap **options = data;
328 union in_addr_union addr;
329 DHCPOptionDataType type;
330 uint8_t u8, uint8_data;
331 uint16_t u16, uint16_data;
332 uint32_t uint32_data;
333 const void *udata;
334 const char *p;
335 ssize_t sz;
336 int r;
337
338 assert(filename);
339 assert(lvalue);
340 assert(rvalue);
341 assert(data);
342
343 if (isempty(rvalue)) {
344 *options = ordered_hashmap_free(*options);
345 return 0;
346 }
347
348 p = rvalue;
349 r = extract_first_word(&p, &word, ":", 0);
350 if (r == -ENOMEM)
351 return log_oom();
352 if (r <= 0) {
353 log_syntax(unit, LOG_ERR, filename, line, r,
354 "Invalid DHCP option, ignoring assignment: %s", rvalue);
355 return 0;
356 }
357
358 if (ltype == AF_INET6) {
359 r = safe_atou16(word, &u16);
360 if (r < 0) {
361 log_syntax(unit, LOG_ERR, filename, line, r,
362 "Invalid DHCP option, ignoring assignment: %s", rvalue);
363 return 0;
364 }
365 if (u16 < 1 || u16 >= 65535) {
366 log_syntax(unit, LOG_ERR, filename, line, 0,
367 "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
368 return 0;
369 }
370 } else {
371 r = safe_atou8(word, &u8);
372 if (r < 0) {
373 log_syntax(unit, LOG_ERR, filename, line, r,
374 "Invalid DHCP option, ignoring assignment: %s", rvalue);
375 return 0;
376 }
377 if (u8 < 1 || u8 >= 255) {
378 log_syntax(unit, LOG_ERR, filename, line, 0,
379 "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
380 return 0;
381 }
382 }
383
384 word = mfree(word);
385 r = extract_first_word(&p, &word, ":", 0);
386 if (r == -ENOMEM)
387 return log_oom();
388 if (r <= 0) {
389 log_syntax(unit, LOG_ERR, filename, line, r,
390 "Invalid DHCP option, ignoring assignment: %s", rvalue);
391 return 0;
392 }
393
394 type = dhcp_option_data_type_from_string(word);
395 if (type < 0) {
396 log_syntax(unit, LOG_ERR, filename, line, 0,
397 "Invalid DHCP option data type, ignoring assignment: %s", p);
398 return 0;
399 }
400
401 switch(type) {
402 case DHCP_OPTION_DATA_UINT8:{
403 r = safe_atou8(p, &uint8_data);
404 if (r < 0) {
405 log_syntax(unit, LOG_ERR, filename, line, r,
406 "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
407 return 0;
408 }
409
410 udata = &uint8_data;
411 sz = sizeof(uint8_t);
412 break;
413 }
414 case DHCP_OPTION_DATA_UINT16:{
415 r = safe_atou16(p, &uint16_data);
416 if (r < 0) {
417 log_syntax(unit, LOG_ERR, filename, line, r,
418 "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
419 return 0;
420 }
421
422 udata = &uint16_data;
423 sz = sizeof(uint16_t);
424 break;
425 }
426 case DHCP_OPTION_DATA_UINT32: {
427 r = safe_atou32(p, &uint32_data);
428 if (r < 0) {
429 log_syntax(unit, LOG_ERR, filename, line, r,
430 "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
431 return 0;
432 }
433
434 udata = &uint32_data;
435 sz = sizeof(uint32_t);
436
437 break;
438 }
439 case DHCP_OPTION_DATA_IPV4ADDRESS: {
440 r = in_addr_from_string(AF_INET, p, &addr);
441 if (r < 0) {
442 log_syntax(unit, LOG_ERR, filename, line, r,
443 "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
444 return 0;
445 }
446
447 udata = &addr.in;
448 sz = sizeof(addr.in.s_addr);
449 break;
450 }
451 case DHCP_OPTION_DATA_IPV6ADDRESS: {
452 r = in_addr_from_string(AF_INET6, p, &addr);
453 if (r < 0) {
454 log_syntax(unit, LOG_ERR, filename, line, r,
455 "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
456 return 0;
457 }
458
459 udata = &addr.in6;
460 sz = sizeof(addr.in6.s6_addr);
461 break;
462 }
463 case DHCP_OPTION_DATA_STRING:
464 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
465 if (sz < 0) {
466 log_syntax(unit, LOG_ERR, filename, line, sz,
467 "Failed to decode DHCP option data, ignoring assignment: %s", p);
468 }
469
470 udata = q;
471 break;
472 default:
473 return -EINVAL;
474 }
475
476 if (ltype == AF_INET6) {
477 r = sd_dhcp6_option_new(u16, udata, sz, &opt6);
478 if (r < 0) {
479 log_syntax(unit, LOG_ERR, filename, line, r,
480 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
481 return 0;
482 }
483
484 r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
485 if (r < 0)
486 return log_oom();
487
488 /* Overwrite existing option */
489 old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
490 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
491 if (r < 0) {
492 log_syntax(unit, LOG_ERR, filename, line, r,
493 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
494 return 0;
495 }
496 TAKE_PTR(opt6);
497 } else {
498 r = sd_dhcp_option_new(u8, udata, sz, &opt4);
499 if (r < 0) {
500 log_syntax(unit, LOG_ERR, filename, line, r,
501 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
502 return 0;
503 }
504
505 r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
506 if (r < 0)
507 return log_oom();
508
509 /* Overwrite existing option */
510 old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
511 r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
512 if (r < 0) {
513 log_syntax(unit, LOG_ERR, filename, line, r,
514 "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
515 return 0;
516 }
517 TAKE_PTR(opt4);
518 }
519 return 0;
520 }
521
522 int config_parse_dhcp_request_options(
523 const char *unit,
524 const char *filename,
525 unsigned line,
526 const char *section,
527 unsigned section_line,
528 const char *lvalue,
529 int ltype,
530 const char *rvalue,
531 void *data,
532 void *userdata) {
533
534 Network *network = data;
535 const char *p;
536 int r;
537
538 assert(filename);
539 assert(lvalue);
540 assert(rvalue);
541 assert(data);
542
543 if (isempty(rvalue)) {
544 if (ltype == AF_INET)
545 network->dhcp_request_options = set_free(network->dhcp_request_options);
546 else
547 network->dhcp6_request_options = set_free(network->dhcp6_request_options);
548
549 return 0;
550 }
551
552 for (p = rvalue;;) {
553 _cleanup_free_ char *n = NULL;
554 uint32_t i;
555
556 r = extract_first_word(&p, &n, NULL, 0);
557 if (r < 0) {
558 log_syntax(unit, LOG_ERR, filename, line, r,
559 "Failed to parse DHCP request option, ignoring assignment: %s",
560 rvalue);
561 return 0;
562 }
563 if (r == 0)
564 return 0;
565
566 r = safe_atou32(n, &i);
567 if (r < 0) {
568 log_syntax(unit, LOG_ERR, filename, line, r,
569 "DHCP request option is invalid, ignoring assignment: %s", n);
570 continue;
571 }
572
573 if (i < 1 || i >= 255) {
574 log_syntax(unit, LOG_ERR, filename, line, r,
575 "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
576 continue;
577 }
578
579 if (ltype == AF_INET)
580 r = set_ensure_allocated(&network->dhcp_request_options, NULL);
581 else
582 r = set_ensure_allocated(&network->dhcp6_request_options, NULL);
583 if (r < 0)
584 return log_oom();
585
586 if (ltype == AF_INET)
587 r = set_put(network->dhcp_request_options, UINT32_TO_PTR(i));
588 else
589 r = set_put(network->dhcp6_request_options, UINT32_TO_PTR(i));
590 if (r < 0)
591 log_syntax(unit, LOG_ERR, filename, line, r,
592 "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
593 }
594
595 return 0;
596 }
597
598 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
599 "Failed to parse DHCP use domains setting");
600
601 static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
602 [DHCP_USE_DOMAINS_NO] = "no",
603 [DHCP_USE_DOMAINS_ROUTE] = "route",
604 [DHCP_USE_DOMAINS_YES] = "yes",
605 };
606
607 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
608
609 static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
610 [DHCP_OPTION_DATA_UINT8] = "uint8",
611 [DHCP_OPTION_DATA_UINT16] = "uint16",
612 [DHCP_OPTION_DATA_UINT32] = "uint32",
613 [DHCP_OPTION_DATA_STRING] = "string",
614 [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
615 [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
616 };
617
618 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);