]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-dhcp-server.c
tree-wide: fix spelling errors
[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 for (i = 0; i < link->network->n_dns; i++) {
49 struct in_addr ia;
50
51 /* Only look for IPv4 addresses */
52 if (link->network->dns[i].family != AF_INET)
53 continue;
54
55 ia = link->network->dns[i].address.in;
56
57 /* Never propagate obviously borked data */
58 if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
59 continue;
60
61 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
62 return log_oom();
63
64 addresses[n_addresses++] = ia;
65 }
66
67 if (link->network->dhcp_use_dns && link->dhcp_lease) {
68 const struct in_addr *da = NULL;
69 int j, n;
70
71 n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
72 if (n > 0) {
73
74 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
75 return log_oom();
76
77 for (j = 0; j < n; j++)
78 if (in4_addr_is_non_local(&da[j]))
79 addresses[n_addresses++] = da[j];
80 }
81 }
82
83 if (n_addresses <= 0)
84 return 0;
85
86 return sd_dhcp_server_set_dns(s, addresses, n_addresses);
87 }
88
89 static int link_push_uplink_to_dhcp_server(
90 Link *link,
91 sd_dhcp_lease_info what,
92 sd_dhcp_server *s) {
93
94 _cleanup_free_ struct in_addr *addresses = NULL;
95 size_t n_addresses = 0, n_allocated = 0;
96 bool lease_condition;
97 char **servers;
98
99 if (!link->network)
100 return 0;
101
102 log_link_debug(link, "Copying %s from link", dhcp_lease_info_to_string(what));
103
104 switch (what) {
105 case SD_DHCP_LEASE_DNS_SERVERS:
106 /* DNS servers are stored as parsed data, so special handling is required.
107 * TODO: check if DNS servers should be stored unparsed too. */
108 return link_push_uplink_dns_to_dhcp_server(link, s);
109
110 case SD_DHCP_LEASE_NTP_SERVERS:
111 servers = link->network->ntp;
112 lease_condition = link->network->dhcp_use_ntp;
113 break;
114
115 case SD_DHCP_LEASE_POP3_SERVERS:
116 servers = link->network->pop3;
117 lease_condition = true;
118 break;
119
120 case SD_DHCP_LEASE_SMTP_SERVERS:
121 servers = link->network->smtp;
122 lease_condition = true;
123 break;
124
125 case SD_DHCP_LEASE_SIP_SERVERS:
126 servers = link->network->sip;
127 lease_condition = link->network->dhcp_use_sip;
128 break;
129
130 default:
131 assert_not_reached("Unknown DHCP lease info item");
132 }
133
134 char **a;
135 STRV_FOREACH(a, servers) {
136 union in_addr_union ia;
137
138 /* Only look for IPv4 addresses */
139 if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
140 continue;
141
142 /* Never propagate obviously borked data */
143 if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
144 continue;
145
146 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
147 return log_oom();
148
149 addresses[n_addresses++] = ia.in;
150 }
151
152 if (lease_condition && link->dhcp_lease) {
153 const struct in_addr *da;
154
155 size_t n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
156 if (n > 0) {
157 if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
158 return log_oom();
159
160 for (unsigned i = 0; i < n; i++)
161 if (in4_addr_is_non_local(&da[i]))
162 addresses[n_addresses++] = da[i];
163 }
164 }
165
166 if (n_addresses <= 0)
167 return 0;
168
169 return sd_dhcp_server_set_servers(s, what, addresses, n_addresses);
170 }
171
172 int dhcp4_server_configure(Link *link) {
173 bool acquired_uplink = false;
174 sd_dhcp_option *p;
175 Link *uplink = NULL;
176 Address *address;
177 Iterator i;
178 int r;
179
180 address = link_find_dhcp_server_address(link);
181 if (!address)
182 return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
183 "Failed to find suitable address for DHCPv4 server instance.");
184
185 /* use the server address' subnet as the pool */
186 r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
187 link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
188 if (r < 0)
189 return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
190
191 /* TODO:
192 r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
193 if (r < 0)
194 return r;
195 */
196
197 if (link->network->dhcp_server_max_lease_time_usec > 0) {
198 r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
199 DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
200 if (r < 0)
201 return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
202 }
203
204 if (link->network->dhcp_server_default_lease_time_usec > 0) {
205 r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
206 DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
207 if (r < 0)
208 return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
209 }
210
211 const struct {
212 bool condition;
213 const struct in_addr *servers;
214 unsigned n_servers;
215 } configs[] = {
216 [SD_DHCP_LEASE_DNS_SERVERS] = {
217 link->network->dhcp_server_emit_dns,
218 link->network->dhcp_server_dns,
219 link->network->n_dhcp_server_dns,
220 },
221 [SD_DHCP_LEASE_NTP_SERVERS] = {
222 link->network->dhcp_server_emit_ntp,
223 link->network->dhcp_server_ntp,
224 link->network->n_dhcp_server_ntp,
225 },
226 [SD_DHCP_LEASE_SIP_SERVERS] = {
227 link->network->dhcp_server_emit_sip,
228 link->network->dhcp_server_sip,
229 link->network->n_dhcp_server_sip,
230 },
231 [SD_DHCP_LEASE_POP3_SERVERS] = {
232 true,
233 link->network->dhcp_server_pop3,
234 link->network->n_dhcp_server_pop3,
235 },
236 [SD_DHCP_LEASE_SMTP_SERVERS] = {
237 true,
238 link->network->dhcp_server_smtp,
239 link->network->n_dhcp_server_smtp,
240 },
241 };
242 assert_cc(ELEMENTSOF(configs) == _SD_DHCP_LEASE_INFO_MAX);
243
244 for (unsigned n = 0; n < ELEMENTSOF(configs); n++)
245 if (configs[n].condition) {
246 if (configs[n].n_servers > 0)
247 r = sd_dhcp_server_set_servers(link->dhcp_server, n,
248 configs[n].servers, configs[n].n_servers);
249 else {
250 if (!acquired_uplink) {
251 uplink = manager_find_uplink(link->manager, link);
252 acquired_uplink = true;
253 }
254
255 if (!uplink) {
256 log_link_debug(link,
257 "Not emitting %s on link, couldn't find suitable uplink.",
258 dhcp_lease_info_to_string(n));
259 r = 0;
260 } else
261 r = link_push_uplink_to_dhcp_server(uplink, n, link->dhcp_server);
262 }
263 if (r < 0)
264 log_link_warning_errno(link, r,
265 "Failed to set %s for DHCP server, ignoring: %m",
266 dhcp_lease_info_to_string(n));
267 }
268
269 r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
270 if (r < 0)
271 return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m");
272
273 if (link->network->dhcp_server_emit_timezone) {
274 _cleanup_free_ char *buffer = NULL;
275 const char *tz;
276
277 if (link->network->dhcp_server_timezone)
278 tz = link->network->dhcp_server_timezone;
279 else {
280 r = get_timezone(&buffer);
281 if (r < 0)
282 return log_link_error_errno(link, r, "Failed to determine timezone: %m");
283
284 tz = buffer;
285 }
286
287 r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
288 if (r < 0)
289 return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m");
290 }
291
292 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options, i) {
293 r = sd_dhcp_server_add_option(link->dhcp_server, p);
294 if (r == -EEXIST)
295 continue;
296 if (r < 0)
297 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
298 }
299
300 ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) {
301 r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p);
302 if (r == -EEXIST)
303 continue;
304 if (r < 0)
305 return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
306 }
307
308 if (!sd_dhcp_server_is_running(link->dhcp_server)) {
309 r = sd_dhcp_server_start(link->dhcp_server);
310 if (r < 0)
311 return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
312 }
313
314 return 0;
315 }
316
317 static int config_parse_dhcp_lease_server_list(
318 const char *unit,
319 const char *filename,
320 unsigned line,
321 const char *lvalue,
322 const char *rvalue,
323 struct in_addr **addresses,
324 unsigned *n_addresses) {
325
326 assert(filename);
327 assert(lvalue);
328 assert(rvalue);
329
330 for (const char *p = rvalue;;) {
331 _cleanup_free_ char *w = NULL;
332 union in_addr_union a;
333 int r;
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 return 0;
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 %s= address '%s', ignoring: %m", lvalue, w);
350 continue;
351 }
352
353 struct in_addr *m = reallocarray(*addresses, *n_addresses + 1, sizeof(struct in_addr));
354 if (!m)
355 return log_oom();
356
357 m[(*n_addresses)++] = a.in;
358 *addresses = m;
359 }
360 }
361
362 int config_parse_dhcp_server_dns(
363 const char *unit,
364 const char *filename,
365 unsigned line,
366 const char *section,
367 unsigned section_line,
368 const char *lvalue,
369 int ltype,
370 const char *rvalue,
371 void *data,
372 void *userdata) {
373
374 Network *n = data;
375
376 return config_parse_dhcp_lease_server_list(unit, filename, line,
377 lvalue, rvalue,
378 &n->dhcp_server_dns, &n->n_dhcp_server_dns);
379 }
380
381 int config_parse_dhcp_server_ntp(
382 const char *unit,
383 const char *filename,
384 unsigned line,
385 const char *section,
386 unsigned section_line,
387 const char *lvalue,
388 int ltype,
389 const char *rvalue,
390 void *data,
391 void *userdata) {
392
393 Network *n = data;
394
395 return config_parse_dhcp_lease_server_list(unit, filename, line,
396 lvalue, rvalue,
397 &n->dhcp_server_ntp, &n->n_dhcp_server_ntp);
398 }
399
400 int config_parse_dhcp_server_sip(
401 const char *unit,
402 const char *filename,
403 unsigned line,
404 const char *section,
405 unsigned section_line,
406 const char *lvalue,
407 int ltype,
408 const char *rvalue,
409 void *data,
410 void *userdata) {
411
412 Network *n = data;
413
414 return config_parse_dhcp_lease_server_list(unit, filename, line,
415 lvalue, rvalue,
416 &n->dhcp_server_sip, &n->n_dhcp_server_sip);
417 }
418
419 int config_parse_dhcp_server_pop3_servers(
420 const char *unit,
421 const char *filename,
422 unsigned line,
423 const char *section,
424 unsigned section_line,
425 const char *lvalue,
426 int ltype,
427 const char *rvalue,
428 void *data,
429 void *userdata) {
430
431 Network *n = data;
432
433 return config_parse_dhcp_lease_server_list(unit, filename, line,
434 lvalue, rvalue,
435 &n->dhcp_server_pop3, &n->n_dhcp_server_pop3);
436 }
437
438 int config_parse_dhcp_server_smtp_servers(
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
452 return config_parse_dhcp_lease_server_list(unit, filename, line,
453 lvalue, rvalue,
454 &n->dhcp_server_smtp, &n->n_dhcp_server_smtp);
455
456 }