]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-dhcp-server.c
libsystemd-network: DHCP add support to emit and retrive DHCP POP3 server
[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
5#include "networkd-dhcp-server.h"
6#include "networkd-link.h"
7#include "networkd-manager.h"
8#include "networkd-network.h"
564ca984 9#include "parse-util.h"
8fcf1d61 10#include "strv.h"
564ca984
SS
11#include "string-table.h"
12#include "string-util.h"
8fcf1d61
YW
13
14static Address* link_find_dhcp_server_address(Link *link) {
15 Address *address;
16
17 assert(link);
18 assert(link->network);
19
20 /* The first statically configured address if there is any */
21 LIST_FOREACH(addresses, address, link->network->static_addresses) {
22
23 if (address->family != AF_INET)
24 continue;
25
26 if (in_addr_is_null(address->family, &address->in_addr))
27 continue;
28
29 return address;
30 }
31
32 /* If that didn't work, find a suitable address we got from the pool */
33 LIST_FOREACH(addresses, address, link->pool_addresses) {
34 if (address->family != AF_INET)
35 continue;
36
37 return address;
38 }
39
40 return NULL;
41}
42
43static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
44 _cleanup_free_ struct in_addr *addresses = NULL;
45 size_t n_addresses = 0, n_allocated = 0;
46 unsigned i;
47
6b2fd86f 48 log_link_debug(link, "Copying DNS server information from link");
8fcf1d61
YW
49
50 if (!link->network)
51 return 0;
52
53 for (i = 0; i < link->network->n_dns; i++) {
54 struct in_addr ia;
55
56 /* Only look for IPv4 addresses */
57 if (link->network->dns[i].family != AF_INET)
58 continue;
59
60 ia = link->network->dns[i].address.in;
61
62 /* Never propagate obviously borked data */
63 if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
64 continue;
65
66 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
67 return log_oom();
68
69 addresses[n_addresses++] = ia;
70 }
71
72 if (link->network->dhcp_use_dns && link->dhcp_lease) {
73 const struct in_addr *da = NULL;
74 int j, n;
75
76 n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
77 if (n > 0) {
78
79 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
80 return log_oom();
81
82 for (j = 0; j < n; j++)
83 if (in4_addr_is_non_local(&da[j]))
84 addresses[n_addresses++] = da[j];
85 }
86 }
87
88 if (n_addresses <= 0)
89 return 0;
90
91 return sd_dhcp_server_set_dns(s, addresses, n_addresses);
92}
93
94static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
95 _cleanup_free_ struct in_addr *addresses = NULL;
96 size_t n_addresses = 0, n_allocated = 0;
97 char **a;
98
99 if (!link->network)
100 return 0;
101
6b2fd86f 102 log_link_debug(link, "Copying NTP server information from link");
8fcf1d61
YW
103
104 STRV_FOREACH(a, link->network->ntp) {
105 union in_addr_union ia;
106
107 /* Only look for IPv4 addresses */
108 if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
109 continue;
110
111 /* Never propagate obviously borked data */
112 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
113 continue;
114
115 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
116 return log_oom();
117
118 addresses[n_addresses++] = ia.in;
119 }
120
121 if (link->network->dhcp_use_ntp && link->dhcp_lease) {
122 const struct in_addr *da = NULL;
123 int j, n;
124
125 n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
126 if (n > 0) {
127
128 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
129 return log_oom();
130
131 for (j = 0; j < n; j++)
132 if (in4_addr_is_non_local(&da[j]))
133 addresses[n_addresses++] = da[j];
134 }
135 }
136
137 if (n_addresses <= 0)
138 return 0;
139
140 return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
141}
142
299d578f
SS
143static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
144 _cleanup_free_ struct in_addr *addresses = NULL;
145 size_t n_addresses = 0, n_allocated = 0;
146 char **a;
147
148 if (!link->network)
149 return 0;
150
6b2fd86f 151 log_link_debug(link, "Copying SIP server information from link");
299d578f
SS
152
153 STRV_FOREACH(a, link->network->sip) {
154 union in_addr_union ia;
155
156 /* Only look for IPv4 addresses */
157 if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
158 continue;
159
160 /* Never propagate obviously borked data */
161 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
162 continue;
163
164 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
165 return log_oom();
166
167 addresses[n_addresses++] = ia.in;
168 }
169
170 if (link->network->dhcp_use_sip && link->dhcp_lease) {
171 const struct in_addr *da = NULL;
172 int j, n;
173
174 n = sd_dhcp_lease_get_sip(link->dhcp_lease, &da);
175 if (n > 0) {
176
177 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
178 return log_oom();
179
180 for (j = 0; j < n; j++)
181 if (in4_addr_is_non_local(&da[j]))
182 addresses[n_addresses++] = da[j];
183 }
184 }
185
186 if (n_addresses <= 0)
187 return 0;
188
189 return sd_dhcp_server_set_sip(s, addresses, n_addresses);
190}
191
8fcf1d61 192int dhcp4_server_configure(Link *link) {
8fcf1d61 193 bool acquired_uplink = false;
461dbb2f 194 sd_dhcp_option *p;
564ca984
SS
195 Link *uplink = NULL;
196 Address *address;
197 Iterator i;
8fcf1d61
YW
198 int r;
199
200 address = link_find_dhcp_server_address(link);
201 if (!address)
c00c3b64
YW
202 return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
203 "Failed to find suitable address for DHCPv4 server instance.");
8fcf1d61
YW
204
205 /* use the server address' subnet as the pool */
206 r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
207 link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
208 if (r < 0)
c00c3b64 209 return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
8fcf1d61
YW
210
211 /* TODO:
212 r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
213 if (r < 0)
214 return r;
215 */
216
217 if (link->network->dhcp_server_max_lease_time_usec > 0) {
218 r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
219 DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
220 if (r < 0)
c00c3b64 221 return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
8fcf1d61
YW
222 }
223
224 if (link->network->dhcp_server_default_lease_time_usec > 0) {
225 r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
226 DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
227 if (r < 0)
c00c3b64 228 return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
8fcf1d61
YW
229 }
230
231 if (link->network->dhcp_server_emit_dns) {
232 if (link->network->n_dhcp_server_dns > 0)
233 r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
234 else {
235 uplink = manager_find_uplink(link->manager, link);
236 acquired_uplink = true;
237
238 if (!uplink) {
239 log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
240 r = 0;
241 } else
242 r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
243 }
244 if (r < 0)
245 log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
246 }
247
248 if (link->network->dhcp_server_emit_ntp) {
249 if (link->network->n_dhcp_server_ntp > 0)
250 r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
251 else {
252 if (!acquired_uplink)
253 uplink = manager_find_uplink(link->manager, link);
254
255 if (!uplink) {
256 log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
257 r = 0;
258 } else
259 r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
260
261 }
262 if (r < 0)
263 log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
264 }
265
299d578f
SS
266 if (link->network->dhcp_server_emit_sip) {
267 if (link->network->n_dhcp_server_sip > 0)
268 r = sd_dhcp_server_set_sip(link->dhcp_server, link->network->dhcp_server_sip, link->network->n_dhcp_server_sip);
269 else {
270 if (!acquired_uplink)
271 uplink = manager_find_uplink(link->manager, link);
272
273 if (!uplink) {
274 log_link_debug(link, "Not emitting sip server information on link, couldn't find suitable uplink.");
275 r = 0;
276 } else
277 r = link_push_uplink_sip_to_dhcp_server(uplink, link->dhcp_server);
278
279 }
280 if (r < 0)
281 log_link_warning_errno(link, r, "Failed to set SIP server for DHCP server, ignoring: %m");
282 }
283
8fcf1d61
YW
284 r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
285 if (r < 0)
a0fa3ef7 286 return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
8fcf1d61
YW
287
288 if (link->network->dhcp_server_emit_timezone) {
289 _cleanup_free_ char *buffer = NULL;
290 const char *tz;
291
292 if (link->network->dhcp_server_timezone)
293 tz = link->network->dhcp_server_timezone;
294 else {
295 r = get_timezone(&buffer);
296 if (r < 0)
98b02994 297 return log_link_error_errno(link, r, "Failed to determine timezone: %m");
8fcf1d61
YW
298
299 tz = buffer;
300 }
301
302 r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
303 if (r < 0)
c00c3b64 304 return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m");
8fcf1d61 305 }
564ca984 306
0e96961d 307 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options, i) {
461dbb2f 308 r = sd_dhcp_server_add_option(link->dhcp_server, p);
564ca984
SS
309 if (r == -EEXIST)
310 continue;
311 if (r < 0)
c00c3b64 312 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
564ca984
SS
313 }
314
7354900d
DW
315 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) {
316 r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p);
317 if (r == -EEXIST)
318 continue;
319 if (r < 0)
320 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
321 }
322
8fcf1d61
YW
323 if (!sd_dhcp_server_is_running(link->dhcp_server)) {
324 r = sd_dhcp_server_start(link->dhcp_server);
325 if (r < 0)
a0fa3ef7 326 return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
8fcf1d61
YW
327 }
328
329 return 0;
330}
331
332int config_parse_dhcp_server_dns(
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
344 Network *n = data;
345 const char *p = rvalue;
346 int r;
347
348 assert(filename);
349 assert(lvalue);
350 assert(rvalue);
351
352 for (;;) {
353 _cleanup_free_ char *w = NULL;
354 union in_addr_union a;
355 struct in_addr *m;
356
357 r = extract_first_word(&p, &w, NULL, 0);
358 if (r == -ENOMEM)
359 return log_oom();
360 if (r < 0) {
361 log_syntax(unit, LOG_ERR, filename, line, r,
362 "Failed to extract word, ignoring: %s", rvalue);
363 return 0;
364 }
365 if (r == 0)
366 break;
367
368 r = in_addr_from_string(AF_INET, w, &a);
369 if (r < 0) {
370 log_syntax(unit, LOG_ERR, filename, line, r,
371 "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
372 continue;
373 }
374
375 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
376 if (!m)
377 return log_oom();
378
379 m[n->n_dhcp_server_dns++] = a.in;
380 n->dhcp_server_dns = m;
381 }
382
383 return 0;
384}
385
386int config_parse_dhcp_server_ntp(
387 const char *unit,
388 const char *filename,
389 unsigned line,
390 const char *section,
391 unsigned section_line,
392 const char *lvalue,
393 int ltype,
394 const char *rvalue,
395 void *data,
396 void *userdata) {
397
398 Network *n = data;
399 const char *p = rvalue;
400 int r;
401
402 assert(filename);
403 assert(lvalue);
404 assert(rvalue);
405
406 for (;;) {
407 _cleanup_free_ char *w = NULL;
408 union in_addr_union a;
409 struct in_addr *m;
410
411 r = extract_first_word(&p, &w, NULL, 0);
412 if (r == -ENOMEM)
413 return log_oom();
414 if (r < 0) {
415 log_syntax(unit, LOG_ERR, filename, line, r,
416 "Failed to extract word, ignoring: %s", rvalue);
417 return 0;
418 }
419 if (r == 0)
420 return 0;
421
422 r = in_addr_from_string(AF_INET, w, &a);
423 if (r < 0) {
424 log_syntax(unit, LOG_ERR, filename, line, r,
425 "Failed to parse NTP server address '%s', ignoring: %m", w);
426 continue;
427 }
428
429 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
430 if (!m)
431 return log_oom();
432
433 m[n->n_dhcp_server_ntp++] = a.in;
434 n->dhcp_server_ntp = m;
435 }
436}
299d578f
SS
437
438int config_parse_dhcp_server_sip(
439 const char *unit,
440 const char *filename,
441 unsigned line,
442 const char *section,
443 unsigned section_line,
444 const char *lvalue,
445 int ltype,
446 const char *rvalue,
447 void *data,
448 void *userdata) {
449
450 Network *n = data;
451 const char *p = rvalue;
452 int r;
453
454 assert(filename);
455 assert(lvalue);
456 assert(rvalue);
457
458 for (;;) {
459 _cleanup_free_ char *w = NULL;
460 union in_addr_union a;
461 struct in_addr *m;
462
463 r = extract_first_word(&p, &w, NULL, 0);
464 if (r == -ENOMEM)
465 return log_oom();
466 if (r < 0) {
467 log_syntax(unit, LOG_ERR, filename, line, r,
468 "Failed to extract word, ignoring: %s", rvalue);
469 return 0;
470 }
471 if (r == 0)
472 return 0;
473
474 r = in_addr_from_string(AF_INET, w, &a);
475 if (r < 0) {
476 log_syntax(unit, LOG_ERR, filename, line, r,
477 "Failed to parse SIP server address '%s', ignoring: %m", w);
478 continue;
479 }
480
481 m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr));
482 if (!m)
483 return log_oom();
484
485 m[n->n_dhcp_server_sip++] = a.in;
486 n->dhcp_server_sip = m;
487 }
488}