]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-server.c
networkd: dhcp server Support Vendor specific 43
[thirdparty/systemd.git] / src / network / networkd-dhcp-server.c
CommitLineData
8fcf1d61
YW
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include "sd-dhcp-server.h"
4
564ca984 5#include "escape.h"
8fcf1d61
YW
6#include "networkd-dhcp-server.h"
7#include "networkd-link.h"
8#include "networkd-manager.h"
9#include "networkd-network.h"
564ca984 10#include "parse-util.h"
8fcf1d61 11#include "strv.h"
564ca984
SS
12#include "string-table.h"
13#include "string-util.h"
8fcf1d61
YW
14
15static Address* link_find_dhcp_server_address(Link *link) {
16 Address *address;
17
18 assert(link);
19 assert(link->network);
20
21 /* The first statically configured address if there is any */
22 LIST_FOREACH(addresses, address, link->network->static_addresses) {
23
24 if (address->family != AF_INET)
25 continue;
26
27 if (in_addr_is_null(address->family, &address->in_addr))
28 continue;
29
30 return address;
31 }
32
33 /* If that didn't work, find a suitable address we got from the pool */
34 LIST_FOREACH(addresses, address, link->pool_addresses) {
35 if (address->family != AF_INET)
36 continue;
37
38 return address;
39 }
40
41 return NULL;
42}
43
44static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
45 _cleanup_free_ struct in_addr *addresses = NULL;
46 size_t n_addresses = 0, n_allocated = 0;
47 unsigned i;
48
49 log_debug("Copying DNS server information from %s", link->ifname);
50
51 if (!link->network)
52 return 0;
53
54 for (i = 0; i < link->network->n_dns; i++) {
55 struct in_addr ia;
56
57 /* Only look for IPv4 addresses */
58 if (link->network->dns[i].family != AF_INET)
59 continue;
60
61 ia = link->network->dns[i].address.in;
62
63 /* Never propagate obviously borked data */
64 if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
65 continue;
66
67 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
68 return log_oom();
69
70 addresses[n_addresses++] = ia;
71 }
72
73 if (link->network->dhcp_use_dns && link->dhcp_lease) {
74 const struct in_addr *da = NULL;
75 int j, n;
76
77 n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
78 if (n > 0) {
79
80 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
81 return log_oom();
82
83 for (j = 0; j < n; j++)
84 if (in4_addr_is_non_local(&da[j]))
85 addresses[n_addresses++] = da[j];
86 }
87 }
88
89 if (n_addresses <= 0)
90 return 0;
91
92 return sd_dhcp_server_set_dns(s, addresses, n_addresses);
93}
94
95static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
96 _cleanup_free_ struct in_addr *addresses = NULL;
97 size_t n_addresses = 0, n_allocated = 0;
98 char **a;
99
100 if (!link->network)
101 return 0;
102
103 log_debug("Copying NTP server information from %s", link->ifname);
104
105 STRV_FOREACH(a, link->network->ntp) {
106 union in_addr_union ia;
107
108 /* Only look for IPv4 addresses */
109 if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
110 continue;
111
112 /* Never propagate obviously borked data */
113 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
114 continue;
115
116 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
117 return log_oom();
118
119 addresses[n_addresses++] = ia.in;
120 }
121
122 if (link->network->dhcp_use_ntp && link->dhcp_lease) {
123 const struct in_addr *da = NULL;
124 int j, n;
125
126 n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
127 if (n > 0) {
128
129 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
130 return log_oom();
131
132 for (j = 0; j < n; j++)
133 if (in4_addr_is_non_local(&da[j]))
134 addresses[n_addresses++] = da[j];
135 }
136 }
137
138 if (n_addresses <= 0)
139 return 0;
140
141 return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
142}
143
299d578f
SS
144static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
145 _cleanup_free_ struct in_addr *addresses = NULL;
146 size_t n_addresses = 0, n_allocated = 0;
147 char **a;
148
149 if (!link->network)
150 return 0;
151
152 log_debug("Copying SIP server information from %s", link->ifname);
153
154 STRV_FOREACH(a, link->network->sip) {
155 union in_addr_union ia;
156
157 /* Only look for IPv4 addresses */
158 if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
159 continue;
160
161 /* Never propagate obviously borked data */
162 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
163 continue;
164
165 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
166 return log_oom();
167
168 addresses[n_addresses++] = ia.in;
169 }
170
171 if (link->network->dhcp_use_sip && link->dhcp_lease) {
172 const struct in_addr *da = NULL;
173 int j, n;
174
175 n = sd_dhcp_lease_get_sip(link->dhcp_lease, &da);
176 if (n > 0) {
177
178 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
179 return log_oom();
180
181 for (j = 0; j < n; j++)
182 if (in4_addr_is_non_local(&da[j]))
183 addresses[n_addresses++] = da[j];
184 }
185 }
186
187 if (n_addresses <= 0)
188 return 0;
189
190 return sd_dhcp_server_set_sip(s, addresses, n_addresses);
191}
192
8fcf1d61 193int dhcp4_server_configure(Link *link) {
8fcf1d61 194 bool acquired_uplink = false;
564ca984
SS
195 sd_dhcp_raw_option *p;
196 Link *uplink = NULL;
197 Address *address;
198 Iterator i;
8fcf1d61
YW
199 int r;
200
201 address = link_find_dhcp_server_address(link);
202 if (!address)
203 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBUSY),
204 "Failed to find suitable address for DHCPv4 server instance.");
205
206 /* use the server address' subnet as the pool */
207 r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
208 link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
209 if (r < 0)
210 return r;
211
212 /* TODO:
213 r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
214 if (r < 0)
215 return r;
216 */
217
218 if (link->network->dhcp_server_max_lease_time_usec > 0) {
219 r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
220 DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
221 if (r < 0)
222 return r;
223 }
224
225 if (link->network->dhcp_server_default_lease_time_usec > 0) {
226 r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
227 DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
228 if (r < 0)
229 return r;
230 }
231
232 if (link->network->dhcp_server_emit_dns) {
233 if (link->network->n_dhcp_server_dns > 0)
234 r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
235 else {
236 uplink = manager_find_uplink(link->manager, link);
237 acquired_uplink = true;
238
239 if (!uplink) {
240 log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
241 r = 0;
242 } else
243 r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
244 }
245 if (r < 0)
246 log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
247 }
248
249 if (link->network->dhcp_server_emit_ntp) {
250 if (link->network->n_dhcp_server_ntp > 0)
251 r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
252 else {
253 if (!acquired_uplink)
254 uplink = manager_find_uplink(link->manager, link);
255
256 if (!uplink) {
257 log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
258 r = 0;
259 } else
260 r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
261
262 }
263 if (r < 0)
264 log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
265 }
266
299d578f
SS
267 if (link->network->dhcp_server_emit_sip) {
268 if (link->network->n_dhcp_server_sip > 0)
269 r = sd_dhcp_server_set_sip(link->dhcp_server, link->network->dhcp_server_sip, link->network->n_dhcp_server_sip);
270 else {
271 if (!acquired_uplink)
272 uplink = manager_find_uplink(link->manager, link);
273
274 if (!uplink) {
275 log_link_debug(link, "Not emitting sip server information on link, couldn't find suitable uplink.");
276 r = 0;
277 } else
278 r = link_push_uplink_sip_to_dhcp_server(uplink, link->dhcp_server);
279
280 }
281 if (r < 0)
282 log_link_warning_errno(link, r, "Failed to set SIP server for DHCP server, ignoring: %m");
283 }
284
8fcf1d61
YW
285 r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
286 if (r < 0)
287 return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
288
289 if (link->network->dhcp_server_emit_timezone) {
290 _cleanup_free_ char *buffer = NULL;
291 const char *tz;
292
293 if (link->network->dhcp_server_timezone)
294 tz = link->network->dhcp_server_timezone;
295 else {
296 r = get_timezone(&buffer);
297 if (r < 0)
298 return log_warning_errno(r, "Failed to determine timezone: %m");
299
300 tz = buffer;
301 }
302
303 r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
304 if (r < 0)
305 return r;
306 }
564ca984
SS
307
308 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_raw_options, i) {
309 r = sd_dhcp_server_add_raw_option(link->dhcp_server, p);
310 if (r == -EEXIST)
311 continue;
312 if (r < 0)
313 return r;
314 }
315
8fcf1d61
YW
316 if (!sd_dhcp_server_is_running(link->dhcp_server)) {
317 r = sd_dhcp_server_start(link->dhcp_server);
318 if (r < 0)
319 return log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
320 }
321
322 return 0;
323}
324
325int config_parse_dhcp_server_dns(
326 const char *unit,
327 const char *filename,
328 unsigned line,
329 const char *section,
330 unsigned section_line,
331 const char *lvalue,
332 int ltype,
333 const char *rvalue,
334 void *data,
335 void *userdata) {
336
337 Network *n = data;
338 const char *p = rvalue;
339 int r;
340
341 assert(filename);
342 assert(lvalue);
343 assert(rvalue);
344
345 for (;;) {
346 _cleanup_free_ char *w = NULL;
347 union in_addr_union a;
348 struct in_addr *m;
349
350 r = extract_first_word(&p, &w, NULL, 0);
351 if (r == -ENOMEM)
352 return log_oom();
353 if (r < 0) {
354 log_syntax(unit, LOG_ERR, filename, line, r,
355 "Failed to extract word, ignoring: %s", rvalue);
356 return 0;
357 }
358 if (r == 0)
359 break;
360
361 r = in_addr_from_string(AF_INET, w, &a);
362 if (r < 0) {
363 log_syntax(unit, LOG_ERR, filename, line, r,
364 "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
365 continue;
366 }
367
368 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
369 if (!m)
370 return log_oom();
371
372 m[n->n_dhcp_server_dns++] = a.in;
373 n->dhcp_server_dns = m;
374 }
375
376 return 0;
377}
378
379int config_parse_dhcp_server_ntp(
380 const char *unit,
381 const char *filename,
382 unsigned line,
383 const char *section,
384 unsigned section_line,
385 const char *lvalue,
386 int ltype,
387 const char *rvalue,
388 void *data,
389 void *userdata) {
390
391 Network *n = data;
392 const char *p = rvalue;
393 int r;
394
395 assert(filename);
396 assert(lvalue);
397 assert(rvalue);
398
399 for (;;) {
400 _cleanup_free_ char *w = NULL;
401 union in_addr_union a;
402 struct in_addr *m;
403
404 r = extract_first_word(&p, &w, NULL, 0);
405 if (r == -ENOMEM)
406 return log_oom();
407 if (r < 0) {
408 log_syntax(unit, LOG_ERR, filename, line, r,
409 "Failed to extract word, ignoring: %s", rvalue);
410 return 0;
411 }
412 if (r == 0)
413 return 0;
414
415 r = in_addr_from_string(AF_INET, w, &a);
416 if (r < 0) {
417 log_syntax(unit, LOG_ERR, filename, line, r,
418 "Failed to parse NTP server address '%s', ignoring: %m", w);
419 continue;
420 }
421
422 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
423 if (!m)
424 return log_oom();
425
426 m[n->n_dhcp_server_ntp++] = a.in;
427 n->dhcp_server_ntp = m;
428 }
429}
299d578f
SS
430
431int config_parse_dhcp_server_sip(
432 const char *unit,
433 const char *filename,
434 unsigned line,
435 const char *section,
436 unsigned section_line,
437 const char *lvalue,
438 int ltype,
439 const char *rvalue,
440 void *data,
441 void *userdata) {
442
443 Network *n = data;
444 const char *p = rvalue;
445 int r;
446
447 assert(filename);
448 assert(lvalue);
449 assert(rvalue);
450
451 for (;;) {
452 _cleanup_free_ char *w = NULL;
453 union in_addr_union a;
454 struct in_addr *m;
455
456 r = extract_first_word(&p, &w, NULL, 0);
457 if (r == -ENOMEM)
458 return log_oom();
459 if (r < 0) {
460 log_syntax(unit, LOG_ERR, filename, line, r,
461 "Failed to extract word, ignoring: %s", rvalue);
462 return 0;
463 }
464 if (r == 0)
465 return 0;
466
467 r = in_addr_from_string(AF_INET, w, &a);
468 if (r < 0) {
469 log_syntax(unit, LOG_ERR, filename, line, r,
470 "Failed to parse SIP server address '%s', ignoring: %m", w);
471 continue;
472 }
473
474 m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr));
475 if (!m)
476 return log_oom();
477
478 m[n->n_dhcp_server_sip++] = a.in;
479 n->dhcp_server_sip = m;
480 }
481}
564ca984
SS
482
483static const char * const dhcp_raw_option_data_type_table[_DHCP_RAW_OPTION_DATA_MAX] = {
484 [DHCP_RAW_OPTION_DATA_UINT8] = "uint8",
485 [DHCP_RAW_OPTION_DATA_UINT16] = "uint16",
486 [DHCP_RAW_OPTION_DATA_UINT32] = "uint32",
487 [DHCP_RAW_OPTION_DATA_STRING] = "string",
488 [DHCP_RAW_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
489};
490
491DEFINE_STRING_TABLE_LOOKUP(dhcp_raw_option_data_type, DHCPRawOption);
492
493int config_parse_dhcp_server_raw_option_data(
494 const char *unit,
495 const char *filename,
496 unsigned line,
497 const char *section,
498 unsigned section_line,
499 const char *lvalue,
500 int ltype,
501 const char *rvalue,
502 void *data,
503 void *userdata) {
504
505 _cleanup_(sd_dhcp_raw_option_unrefp) sd_dhcp_raw_option *opt = NULL, *old = NULL;
506 _cleanup_free_ char *word = NULL, *q = NULL;
507 union in_addr_union addr;
508 Network *network = data;
509 uint16_t uint16_data;
510 uint32_t uint32_data;
511 uint8_t uint8_data;
512 DHCPRawOption type;
513 const char *p;
514 void *udata;
515 ssize_t sz;
516 uint8_t u;
517 int r;
518
519 assert(filename);
520 assert(lvalue);
521 assert(rvalue);
522 assert(data);
523
524 if (isempty(rvalue)) {
525 network->dhcp_server_raw_options = ordered_hashmap_free(network->dhcp_server_raw_options);
526 return 0;
527 }
528
529 p = rvalue;
530 r = extract_first_word(&p, &word, ":", 0);
531 if (r == -ENOMEM)
532 return log_oom();
533 if (r <= 0) {
534 log_syntax(unit, LOG_ERR, filename, line, r,
535 "Invalid DHCP server send raw option, ignoring assignment: %s", rvalue);
536 return 0;
537 }
538
539 r = safe_atou8(word, &u);
540 if (r < 0) {
541 log_syntax(unit, LOG_ERR, filename, line, r,
542 "Failed to parse DHCP server send raw option type, ignoring assignment: %s", rvalue);
543 return 0;
544 }
545 if (u < 1 || u >= 255) {
546 log_syntax(unit, LOG_ERR, filename, line, 0,
547 "Invalid DHCP server send raw option, valid range is 1-254, ignoring assignment: %s", rvalue);
548 return 0;
549 }
550
551 free(word);
552
553 r = extract_first_word(&p, &word, ":", 0);
554 if (r == -ENOMEM)
555 return log_oom();
556 if (r <= 0) {
557 log_syntax(unit, LOG_ERR, filename, line, r,
558 "Invalid DHCP server send raw option, ignoring assignment: %s", rvalue);
559 return 0;
560 }
561
562 r = dhcp_raw_option_data_type_from_string(word);
563 if (r < 0) {
564 log_syntax(unit, LOG_ERR, filename, line, r,
565 "Invalid DHCP server send data type, ignoring assignment: %s", p);
566 return 0;
567 }
568
569 type = r;
570 switch(type) {
571 case DHCP_RAW_OPTION_DATA_UINT8:{
572 r = safe_atou8(p, &uint8_data);
573 if (r < 0) {
574 log_syntax(unit, LOG_ERR, filename, line, r,
575 "Failed to parse DHCPv4 vendor specific uint8 data, ignoring assignment: %s", p);
576 return 0;
577 }
578
579 udata = &uint8_data;
580 sz = sizeof(uint8_t);
581 break;
582 }
583 case DHCP_RAW_OPTION_DATA_UINT16:{
584 r = safe_atou16(p, &uint16_data);
585 if (r < 0) {
586 log_syntax(unit, LOG_ERR, filename, line, r,
587 "Failed to parse DHCPv4 vendor specific uint16 data, ignoring assignment: %s", p);
588 return 0;
589 }
590
591 udata = &uint16_data;
592 sz = sizeof(uint16_t);
593 break;
594 }
595 case DHCP_RAW_OPTION_DATA_UINT32: {
596 r = safe_atou32(p, &uint32_data);
597 if (r < 0) {
598 log_syntax(unit, LOG_ERR, filename, line, r,
599 "Failed to parse DHCPv4 vendor specific uint32 data, ignoring assignment: %s", p);
600 return 0;
601 }
602
603 udata = &uint32_data;
604 sz = sizeof(uint32_t);
605
606 break;
607 }
608 case DHCP_RAW_OPTION_DATA_IPV4ADDRESS: {
609 r = in_addr_from_string(AF_INET, p, &addr);
610 if (r < 0) {
611 log_syntax(unit, LOG_ERR, filename, line, r,
612 "Failed to parse DHCPv4 vendor specific ipv4address data, ignoring assignment: %s", p);
613 return 0;
614 }
615
616 udata = &addr.in;
617 sz = sizeof(addr.in.s_addr);
618 break;
619 }
620 case DHCP_RAW_OPTION_DATA_STRING:
621 sz = cunescape(p, 0, &q);
622 if (sz < 0) {
623 log_syntax(unit, LOG_ERR, filename, line, sz,
624 "Failed to decode option data, ignoring assignment: %s", p);
625 }
626
627 udata = q;
628 break;
629 default:
630 return -EINVAL;
631 }
632
633 r = sd_dhcp_raw_option_new(u, udata, sz, &opt);
634 if (r < 0) {
635 log_syntax(unit, LOG_ERR, filename, line, r,
636 "Failed to store DHCP send raw option '%s', ignoring assignment: %m", rvalue);
637 return 0;
638 }
639
640 r = ordered_hashmap_ensure_allocated(&network->dhcp_server_raw_options, &dhcp_raw_options_hash_ops);
641 if (r < 0)
642 return log_oom();
643
644 /* Overwrite existing option */
645 old = ordered_hashmap_remove(network->dhcp_server_raw_options, UINT_TO_PTR(u));
646 r = ordered_hashmap_put(network->dhcp_server_raw_options, UINT_TO_PTR(u), opt);
647 if (r < 0) {
648 log_syntax(unit, LOG_ERR, filename, line, r,
649 "Failed to store DHCP server send raw option '%s'", rvalue);
650 return 0;
651 }
652
653 TAKE_PTR(opt);
654
655 return 0;
656}