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