]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp-server.c
Merge pull request #14064 from yuwata/network-unify-send-option-and-send-raw-option
[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 "parse-util.h"
10 #include "strv.h"
11 #include "string-table.h"
12 #include "string-util.h"
13
14 static 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
43 static 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
48 log_debug("Copying DNS server information from %s", link->ifname);
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
94 static 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
102 log_debug("Copying NTP server information from %s", link->ifname);
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
143 static 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
151 log_debug("Copying SIP server information from %s", link->ifname);
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
192 int dhcp4_server_configure(Link *link) {
193 bool acquired_uplink = false;
194 sd_dhcp_option *p;
195 Link *uplink = NULL;
196 Address *address;
197 Iterator i;
198 int r;
199
200 address = link_find_dhcp_server_address(link);
201 if (!address)
202 return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
203 "Failed to find suitable address for DHCPv4 server instance.");
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)
209 return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
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)
221 return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
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)
228 return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
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
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
284 r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
285 if (r < 0)
286 return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
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)
297 return log_error_errno(r, "Failed to determine timezone: %m");
298
299 tz = buffer;
300 }
301
302 r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
303 if (r < 0)
304 return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m");
305 }
306
307 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options, i) {
308 r = sd_dhcp_server_add_option(link->dhcp_server, p);
309 if (r == -EEXIST)
310 continue;
311 if (r < 0)
312 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
313 }
314
315 if (!sd_dhcp_server_is_running(link->dhcp_server)) {
316 r = sd_dhcp_server_start(link->dhcp_server);
317 if (r < 0)
318 return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
319 }
320
321 return 0;
322 }
323
324 int config_parse_dhcp_server_dns(
325 const char *unit,
326 const char *filename,
327 unsigned line,
328 const char *section,
329 unsigned section_line,
330 const char *lvalue,
331 int ltype,
332 const char *rvalue,
333 void *data,
334 void *userdata) {
335
336 Network *n = data;
337 const char *p = rvalue;
338 int r;
339
340 assert(filename);
341 assert(lvalue);
342 assert(rvalue);
343
344 for (;;) {
345 _cleanup_free_ char *w = NULL;
346 union in_addr_union a;
347 struct in_addr *m;
348
349 r = extract_first_word(&p, &w, NULL, 0);
350 if (r == -ENOMEM)
351 return log_oom();
352 if (r < 0) {
353 log_syntax(unit, LOG_ERR, filename, line, r,
354 "Failed to extract word, ignoring: %s", rvalue);
355 return 0;
356 }
357 if (r == 0)
358 break;
359
360 r = in_addr_from_string(AF_INET, w, &a);
361 if (r < 0) {
362 log_syntax(unit, LOG_ERR, filename, line, r,
363 "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
364 continue;
365 }
366
367 m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
368 if (!m)
369 return log_oom();
370
371 m[n->n_dhcp_server_dns++] = a.in;
372 n->dhcp_server_dns = m;
373 }
374
375 return 0;
376 }
377
378 int config_parse_dhcp_server_ntp(
379 const char *unit,
380 const char *filename,
381 unsigned line,
382 const char *section,
383 unsigned section_line,
384 const char *lvalue,
385 int ltype,
386 const char *rvalue,
387 void *data,
388 void *userdata) {
389
390 Network *n = data;
391 const char *p = rvalue;
392 int r;
393
394 assert(filename);
395 assert(lvalue);
396 assert(rvalue);
397
398 for (;;) {
399 _cleanup_free_ char *w = NULL;
400 union in_addr_union a;
401 struct in_addr *m;
402
403 r = extract_first_word(&p, &w, NULL, 0);
404 if (r == -ENOMEM)
405 return log_oom();
406 if (r < 0) {
407 log_syntax(unit, LOG_ERR, filename, line, r,
408 "Failed to extract word, ignoring: %s", rvalue);
409 return 0;
410 }
411 if (r == 0)
412 return 0;
413
414 r = in_addr_from_string(AF_INET, w, &a);
415 if (r < 0) {
416 log_syntax(unit, LOG_ERR, filename, line, r,
417 "Failed to parse NTP server address '%s', ignoring: %m", w);
418 continue;
419 }
420
421 m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
422 if (!m)
423 return log_oom();
424
425 m[n->n_dhcp_server_ntp++] = a.in;
426 n->dhcp_server_ntp = m;
427 }
428 }
429
430 int config_parse_dhcp_server_sip(
431 const char *unit,
432 const char *filename,
433 unsigned line,
434 const char *section,
435 unsigned section_line,
436 const char *lvalue,
437 int ltype,
438 const char *rvalue,
439 void *data,
440 void *userdata) {
441
442 Network *n = data;
443 const char *p = rvalue;
444 int r;
445
446 assert(filename);
447 assert(lvalue);
448 assert(rvalue);
449
450 for (;;) {
451 _cleanup_free_ char *w = NULL;
452 union in_addr_union a;
453 struct in_addr *m;
454
455 r = extract_first_word(&p, &w, NULL, 0);
456 if (r == -ENOMEM)
457 return log_oom();
458 if (r < 0) {
459 log_syntax(unit, LOG_ERR, filename, line, r,
460 "Failed to extract word, ignoring: %s", rvalue);
461 return 0;
462 }
463 if (r == 0)
464 return 0;
465
466 r = in_addr_from_string(AF_INET, w, &a);
467 if (r < 0) {
468 log_syntax(unit, LOG_ERR, filename, line, r,
469 "Failed to parse SIP server address '%s', ignoring: %m", w);
470 continue;
471 }
472
473 m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr));
474 if (!m)
475 return log_oom();
476
477 m[n->n_dhcp_server_sip++] = a.in;
478 n->dhcp_server_sip = m;
479 }
480 }