]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
8fcf1d61 | 2 | |
5ae0fb7f | 3 | #include <netinet/in.h> |
2af02e61 DDM |
4 | #include <net/if.h> |
5 | #include <linux/if_arp.h> /* Must be included after <net/if.h> */ | |
6 | #include <linux/if.h> /* Must be included after <net/if.h> */ | |
5ae0fb7f | 7 | |
8fcf1d61 YW |
8 | #include "sd-dhcp-server.h" |
9 | ||
dd1d3060 MAL |
10 | #include "fd-util.h" |
11 | #include "fileio.h" | |
093e3533 | 12 | #include "networkd-address.h" |
a95e9306 | 13 | #include "networkd-dhcp-server-bus.h" |
c517a49b | 14 | #include "networkd-dhcp-server-static-lease.h" |
15 | #include "networkd-dhcp-server.h" | |
8fcf1d61 YW |
16 | #include "networkd-link.h" |
17 | #include "networkd-manager.h" | |
18 | #include "networkd-network.h" | |
1d28a3cf | 19 | #include "networkd-queue.h" |
344b3cff | 20 | #include "networkd-route-util.h" |
564ca984 | 21 | #include "parse-util.h" |
dd1d3060 | 22 | #include "socket-netlink.h" |
564ca984 SS |
23 | #include "string-table.h" |
24 | #include "string-util.h" | |
dd1d3060 | 25 | #include "strv.h" |
8fcf1d61 | 26 | |
5ae0fb7f YW |
27 | static bool link_dhcp4_server_enabled(Link *link) { |
28 | assert(link); | |
29 | ||
30 | if (link->flags & IFF_LOOPBACK) | |
31 | return false; | |
32 | ||
33 | if (!link->network) | |
34 | return false; | |
35 | ||
5ae0fb7f YW |
36 | if (link->iftype == ARPHRD_CAN) |
37 | return false; | |
38 | ||
39 | return link->network->dhcp_server; | |
40 | } | |
41 | ||
5459e11d YW |
42 | int network_adjust_dhcp_server(Network *network, Set **addresses) { |
43 | int r; | |
44 | ||
0017ba31 | 45 | assert(network); |
5459e11d | 46 | assert(addresses); |
0017ba31 YW |
47 | |
48 | if (!network->dhcp_server) | |
5459e11d | 49 | return 0; |
0017ba31 YW |
50 | |
51 | if (network->bond) { | |
52 | log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.", | |
53 | network->filename); | |
54 | network->dhcp_server = false; | |
5459e11d | 55 | return 0; |
0017ba31 YW |
56 | } |
57 | ||
5459e11d YW |
58 | assert(network->dhcp_server_address_prefixlen <= 32); |
59 | ||
60 | if (network->dhcp_server_address_prefixlen == 0) { | |
0017ba31 | 61 | Address *address; |
5459e11d YW |
62 | |
63 | /* If the server address is not specified, then find suitable static address. */ | |
0017ba31 YW |
64 | |
65 | ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) { | |
26f88471 | 66 | assert(!section_is_invalid(address->section)); |
bab29f2a YW |
67 | |
68 | if (address->family != AF_INET) | |
69 | continue; | |
70 | ||
71 | if (in4_addr_is_localhost(&address->in_addr.in)) | |
72 | continue; | |
73 | ||
74 | if (in4_addr_is_link_local(&address->in_addr.in)) | |
75 | continue; | |
76 | ||
77 | if (in4_addr_is_set(&address->in_addr_peer.in)) | |
78 | continue; | |
79 | ||
5459e11d YW |
80 | /* TODO: check if the prefix length is small enough for the pool. */ |
81 | ||
82 | network->dhcp_server_address = address; | |
bab29f2a | 83 | break; |
0017ba31 | 84 | } |
5459e11d YW |
85 | if (!network->dhcp_server_address) { |
86 | log_warning("%s: DHCPServer= is enabled, but no suitable static address configured. " | |
0017ba31 YW |
87 | "Disabling DHCP server.", |
88 | network->filename); | |
89 | network->dhcp_server = false; | |
5459e11d | 90 | return 0; |
0017ba31 | 91 | } |
3b6a3bde | 92 | |
5459e11d YW |
93 | } else { |
94 | _cleanup_(address_freep) Address *a = NULL; | |
95 | Address *existing; | |
96 | unsigned line; | |
97 | ||
98 | /* TODO: check if the prefix length is small enough for the pool. */ | |
99 | ||
100 | /* If an address is explicitly specified, then check if the corresponding [Address] section | |
101 | * is configured, and add one if not. */ | |
102 | ||
103 | existing = set_get(*addresses, | |
104 | &(Address) { | |
105 | .family = AF_INET, | |
106 | .in_addr.in = network->dhcp_server_address_in_addr, | |
107 | .prefixlen = network->dhcp_server_address_prefixlen, | |
108 | }); | |
109 | if (existing) { | |
110 | /* Corresponding [Address] section already exists. */ | |
111 | network->dhcp_server_address = existing; | |
112 | return 0; | |
113 | } | |
3b6a3bde | 114 | |
5459e11d YW |
115 | r = ordered_hashmap_by_section_find_unused_line(network->addresses_by_section, network->filename, &line); |
116 | if (r < 0) | |
117 | return log_warning_errno(r, "%s: Failed to find unused line number for DHCP server address: %m", | |
118 | network->filename); | |
3b6a3bde | 119 | |
5459e11d YW |
120 | r = address_new_static(network, network->filename, line, &a); |
121 | if (r < 0) | |
122 | return log_warning_errno(r, "%s: Failed to add new static address object for DHCP server: %m", | |
123 | network->filename); | |
8fcf1d61 | 124 | |
5459e11d YW |
125 | a->family = AF_INET; |
126 | a->prefixlen = network->dhcp_server_address_prefixlen; | |
127 | a->in_addr.in = network->dhcp_server_address_in_addr; | |
128 | a->requested_as_null = !in4_addr_is_set(&network->dhcp_server_address_in_addr); | |
8fcf1d61 | 129 | |
5459e11d YW |
130 | r = address_section_verify(a); |
131 | if (r < 0) | |
132 | return r; | |
8fcf1d61 | 133 | |
5459e11d YW |
134 | r = set_ensure_put(addresses, &address_hash_ops, a); |
135 | if (r < 0) | |
136 | return log_oom(); | |
137 | assert(r > 0); | |
3b6a3bde | 138 | |
5459e11d | 139 | network->dhcp_server_address = TAKE_PTR(a); |
3b6a3bde | 140 | } |
8fcf1d61 | 141 | |
5459e11d | 142 | return 0; |
8fcf1d61 YW |
143 | } |
144 | ||
165d7c5c YW |
145 | static int dhcp_server_find_uplink(Link *link, Link **ret) { |
146 | assert(link); | |
147 | ||
148 | if (link->network->dhcp_server_uplink_name) | |
149 | return link_get_by_name(link->manager, link->network->dhcp_server_uplink_name, ret); | |
150 | ||
151 | if (link->network->dhcp_server_uplink_index > 0) | |
6eab614d | 152 | return link_get_by_index(link->manager, link->network->dhcp_server_uplink_index, ret); |
165d7c5c | 153 | |
63295b42 | 154 | if (link->network->dhcp_server_uplink_index == UPLINK_INDEX_AUTO) { |
165d7c5c YW |
155 | /* It is not necessary to propagate error in automatic selection. */ |
156 | if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0) | |
157 | *ret = NULL; | |
158 | return 0; | |
159 | } | |
160 | ||
161 | *ret = NULL; | |
162 | return 0; | |
163 | } | |
164 | ||
2a71d57f LP |
165 | static int link_push_uplink_to_dhcp_server( |
166 | Link *link, | |
2324fd3a | 167 | sd_dhcp_lease_server_type_t what, |
2a71d57f LP |
168 | sd_dhcp_server *s) { |
169 | ||
8fcf1d61 | 170 | _cleanup_free_ struct in_addr *addresses = NULL; |
2a71d57f | 171 | bool use_dhcp_lease_data = true; |
319a4f4b | 172 | size_t n_addresses = 0; |
8fcf1d61 | 173 | |
2a71d57f | 174 | assert(link); |
8fcf1d61 | 175 | |
2a71d57f LP |
176 | if (!link->network) |
177 | return 0; | |
178 | assert(link->network); | |
8fcf1d61 | 179 | |
2a71d57f | 180 | log_link_debug(link, "Copying %s from link", dhcp_lease_server_type_to_string(what)); |
8fcf1d61 | 181 | |
2a71d57f | 182 | switch (what) { |
8fcf1d61 | 183 | |
2a71d57f LP |
184 | case SD_DHCP_LEASE_DNS: |
185 | /* For DNS we have a special case. We the data configured explicitly locally along with the | |
186 | * data from the DHCP lease. */ | |
8fcf1d61 | 187 | |
2a71d57f LP |
188 | for (unsigned i = 0; i < link->network->n_dns; i++) { |
189 | struct in_addr ia; | |
8fcf1d61 | 190 | |
2a71d57f | 191 | /* Only look for IPv4 addresses */ |
e77bd3fd | 192 | if (link->network->dns[i]->family != AF_INET) |
2a71d57f | 193 | continue; |
8fcf1d61 | 194 | |
e77bd3fd | 195 | ia = link->network->dns[i]->address.in; |
2a71d57f LP |
196 | |
197 | /* Never propagate obviously borked data */ | |
198 | if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) | |
199 | continue; | |
200 | ||
319a4f4b | 201 | if (!GREEDY_REALLOC(addresses, n_addresses + 1)) |
8fcf1d61 YW |
202 | return log_oom(); |
203 | ||
2a71d57f | 204 | addresses[n_addresses++] = ia; |
8fcf1d61 | 205 | } |
8fcf1d61 | 206 | |
2a71d57f LP |
207 | use_dhcp_lease_data = link->network->dhcp_use_dns; |
208 | break; | |
8fcf1d61 | 209 | |
2a71d57f | 210 | case SD_DHCP_LEASE_NTP: { |
2a71d57f LP |
211 | /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot |
212 | * propagate via DHCP. Hence let's only propagate those which are IP addresses. */ | |
284e8fd0 | 213 | |
2a71d57f LP |
214 | STRV_FOREACH(i, link->network->ntp) { |
215 | union in_addr_union ia; | |
284e8fd0 | 216 | |
2a71d57f LP |
217 | if (in_addr_from_string(AF_INET, *i, &ia) < 0) |
218 | continue; | |
284e8fd0 | 219 | |
2a71d57f LP |
220 | /* Never propagate obviously borked data */ |
221 | if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) | |
222 | continue; | |
284e8fd0 | 223 | |
319a4f4b | 224 | if (!GREEDY_REALLOC(addresses, n_addresses + 1)) |
2a71d57f | 225 | return log_oom(); |
284e8fd0 | 226 | |
2a71d57f LP |
227 | addresses[n_addresses++] = ia.in; |
228 | } | |
284e8fd0 | 229 | |
2a71d57f | 230 | use_dhcp_lease_data = link->network->dhcp_use_ntp; |
24e6f458 | 231 | break; |
2a71d57f | 232 | } |
284e8fd0 | 233 | |
ddb82ec2 | 234 | case SD_DHCP_LEASE_SIP: |
2a71d57f LP |
235 | |
236 | /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */ | |
237 | use_dhcp_lease_data = link->network->dhcp_use_sip; | |
24e6f458 | 238 | break; |
284e8fd0 | 239 | |
2a71d57f LP |
240 | case SD_DHCP_LEASE_POP3: |
241 | case SD_DHCP_LEASE_SMTP: | |
ddb82ec2 | 242 | case SD_DHCP_LEASE_LPR: |
2a71d57f LP |
243 | /* For the other server types we currently do not allow local configuration of server data, |
244 | * since there are typically no local consumers of the data. */ | |
c4e585a3 | 245 | break; |
d361b373 | 246 | |
24e6f458 | 247 | default: |
04499a70 | 248 | assert_not_reached(); |
f6269fe7 SS |
249 | } |
250 | ||
2a71d57f | 251 | if (use_dhcp_lease_data && link->dhcp_lease) { |
24e6f458 | 252 | const struct in_addr *da; |
f6269fe7 | 253 | |
a2706075 | 254 | int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da); |
f6269fe7 | 255 | if (n > 0) { |
319a4f4b | 256 | if (!GREEDY_REALLOC(addresses, n_addresses + n)) |
f6269fe7 SS |
257 | return log_oom(); |
258 | ||
2a71d57f LP |
259 | for (int j = 0; j < n; j++) |
260 | if (in4_addr_is_non_local(&da[j])) | |
261 | addresses[n_addresses++] = da[j]; | |
f6269fe7 SS |
262 | } |
263 | } | |
264 | ||
265 | if (n_addresses <= 0) | |
266 | return 0; | |
267 | ||
24e6f458 | 268 | return sd_dhcp_server_set_servers(s, what, addresses, n_addresses); |
299d578f SS |
269 | } |
270 | ||
319a4f4b LP |
271 | static int dhcp4_server_parse_dns_server_string_and_warn( |
272 | const char *string, | |
273 | struct in_addr **addresses, | |
274 | size_t *n_addresses) { | |
275 | ||
dd1d3060 MAL |
276 | for (;;) { |
277 | _cleanup_free_ char *word = NULL, *server_name = NULL; | |
278 | union in_addr_union address; | |
279 | int family, r, ifindex = 0; | |
280 | ||
281 | r = extract_first_word(&string, &word, NULL, 0); | |
282 | if (r < 0) | |
283 | return r; | |
284 | if (r == 0) | |
285 | break; | |
286 | ||
287 | r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name); | |
288 | if (r < 0) { | |
289 | log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word); | |
290 | continue; | |
291 | } | |
292 | ||
293 | /* Only look for IPv4 addresses */ | |
294 | if (family != AF_INET) | |
295 | continue; | |
296 | ||
297 | /* Never propagate obviously borked data */ | |
298 | if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in)) | |
299 | continue; | |
300 | ||
319a4f4b | 301 | if (!GREEDY_REALLOC(*addresses, *n_addresses + 1)) |
dd1d3060 MAL |
302 | return log_oom(); |
303 | ||
304 | (*addresses)[(*n_addresses)++] = address.in; | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static int dhcp4_server_set_dns_from_resolve_conf(Link *link) { | |
311 | _cleanup_free_ struct in_addr *addresses = NULL; | |
dd1d3060 | 312 | _cleanup_fclose_ FILE *f = NULL; |
319a4f4b | 313 | size_t n_addresses = 0; |
f8769631 | 314 | int r; |
dd1d3060 MAL |
315 | |
316 | f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re"); | |
317 | if (!f) { | |
318 | if (errno == ENOENT) | |
319 | return 0; | |
320 | ||
321 | return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m"); | |
322 | } | |
323 | ||
324 | for (;;) { | |
325 | _cleanup_free_ char *line = NULL; | |
326 | const char *a; | |
327 | char *l; | |
328 | ||
329 | r = read_line(f, LONG_LINE_MAX, &line); | |
330 | if (r < 0) | |
331 | return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m"); | |
332 | if (r == 0) | |
333 | break; | |
334 | ||
dd1d3060 MAL |
335 | l = strstrip(line); |
336 | if (IN_SET(*l, '#', ';', 0)) | |
337 | continue; | |
338 | ||
339 | a = first_word(l, "nameserver"); | |
340 | if (!a) | |
341 | continue; | |
342 | ||
319a4f4b | 343 | r = dhcp4_server_parse_dns_server_string_and_warn(a, &addresses, &n_addresses); |
dd1d3060 MAL |
344 | if (r < 0) |
345 | log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); | |
346 | } | |
347 | ||
348 | if (n_addresses <= 0) | |
349 | return 0; | |
350 | ||
351 | return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses); | |
352 | } | |
353 | ||
1d28a3cf | 354 | static int dhcp4_server_configure(Link *link) { |
8fcf1d61 | 355 | bool acquired_uplink = false; |
461dbb2f | 356 | sd_dhcp_option *p; |
c517a49b | 357 | DHCPStaticLease *static_lease; |
564ca984 SS |
358 | Link *uplink = NULL; |
359 | Address *address; | |
11c38d3e | 360 | bool bind_to_interface; |
8fcf1d61 YW |
361 | int r; |
362 | ||
5ae0fb7f | 363 | assert(link); |
5459e11d YW |
364 | assert(link->network); |
365 | assert(link->network->dhcp_server_address); | |
5ae0fb7f | 366 | |
1d28a3cf | 367 | log_link_debug(link, "Configuring DHCP Server."); |
5ae0fb7f | 368 | |
1d28a3cf YW |
369 | if (link->dhcp_server) |
370 | return -EBUSY; | |
5ae0fb7f | 371 | |
1d28a3cf YW |
372 | r = sd_dhcp_server_new(&link->dhcp_server, link->ifindex); |
373 | if (r < 0) | |
374 | return r; | |
5ae0fb7f | 375 | |
1d28a3cf YW |
376 | r = sd_dhcp_server_attach_event(link->dhcp_server, link->manager->event, 0); |
377 | if (r < 0) | |
378 | return r; | |
5ae0fb7f | 379 | |
a95e9306 LK |
380 | r = sd_dhcp_server_set_callback(link->dhcp_server, dhcp_server_callback, link); |
381 | if (r < 0) | |
382 | return log_link_warning_errno(link, r, "Failed to set callback for DHCPv4 server instance: %m"); | |
383 | ||
5459e11d | 384 | r = address_get(link, link->network->dhcp_server_address, &address); |
0017ba31 YW |
385 | if (r < 0) |
386 | return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m"); | |
8fcf1d61 YW |
387 | |
388 | /* use the server address' subnet as the pool */ | |
389 | r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, | |
390 | link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); | |
391 | if (r < 0) | |
c00c3b64 | 392 | return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m"); |
8fcf1d61 | 393 | |
8fcf1d61 YW |
394 | if (link->network->dhcp_server_max_lease_time_usec > 0) { |
395 | r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, | |
396 | DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); | |
397 | if (r < 0) | |
c00c3b64 | 398 | return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m"); |
8fcf1d61 YW |
399 | } |
400 | ||
401 | if (link->network->dhcp_server_default_lease_time_usec > 0) { | |
402 | r = sd_dhcp_server_set_default_lease_time(link->dhcp_server, | |
403 | DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); | |
404 | if (r < 0) | |
c00c3b64 | 405 | return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m"); |
8fcf1d61 YW |
406 | } |
407 | ||
6278e428 | 408 | r = sd_dhcp_server_set_boot_server_address(link->dhcp_server, &link->network->dhcp_server_boot_server_address); |
369ac192 | 409 | if (r < 0) |
6278e428 | 410 | return log_link_warning_errno(link, r, "Failed to set boot server address for DHCPv4 server instance: %m"); |
369ac192 | 411 | |
6278e428 | 412 | r = sd_dhcp_server_set_boot_server_name(link->dhcp_server, link->network->dhcp_server_boot_server_name); |
369ac192 | 413 | if (r < 0) |
6278e428 YW |
414 | return log_link_warning_errno(link, r, "Failed to set boot server name for DHCPv4 server instance: %m"); |
415 | ||
416 | r = sd_dhcp_server_set_boot_filename(link->dhcp_server, link->network->dhcp_server_boot_filename); | |
417 | if (r < 0) | |
418 | return log_link_warning_errno(link, r, "Failed to set boot filename for DHCPv4 server instance: %m"); | |
369ac192 | 419 | |
2324fd3a | 420 | for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) { |
2a71d57f LP |
421 | |
422 | if (!link->network->dhcp_server_emit[type].emit) | |
423 | continue; | |
424 | ||
425 | if (link->network->dhcp_server_emit[type].n_addresses > 0) | |
426 | /* Explicitly specified servers to emit */ | |
427 | r = sd_dhcp_server_set_servers( | |
428 | link->dhcp_server, | |
429 | type, | |
430 | link->network->dhcp_server_emit[type].addresses, | |
431 | link->network->dhcp_server_emit[type].n_addresses); | |
432 | else { | |
433 | /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */ | |
434 | if (!acquired_uplink) { | |
165d7c5c | 435 | (void) dhcp_server_find_uplink(link, &uplink); |
2a71d57f LP |
436 | acquired_uplink = true; |
437 | } | |
438 | ||
439 | if (uplink && uplink->network) | |
440 | r = link_push_uplink_to_dhcp_server(uplink, type, link->dhcp_server); | |
441 | else if (type == SD_DHCP_LEASE_DNS) | |
442 | r = dhcp4_server_set_dns_from_resolve_conf(link); | |
24e6f458 | 443 | else { |
2a71d57f LP |
444 | log_link_debug(link, |
445 | "Not emitting %s on link, couldn't find suitable uplink.", | |
446 | dhcp_lease_server_type_to_string(type)); | |
447 | continue; | |
24e6f458 | 448 | } |
299d578f | 449 | } |
284e8fd0 | 450 | |
2a71d57f LP |
451 | if (r < 0) |
452 | log_link_warning_errno(link, r, | |
453 | "Failed to set %s for DHCP server, ignoring: %m", | |
454 | dhcp_lease_server_type_to_string(type)); | |
455 | } | |
456 | ||
59aa6220 YW |
457 | if (link->network->dhcp_server_emit_router) { |
458 | r = sd_dhcp_server_set_router(link->dhcp_server, &link->network->dhcp_server_router); | |
459 | if (r < 0) | |
460 | return log_link_error_errno(link, r, "Failed to set router address for DHCP server: %m"); | |
461 | } | |
8fcf1d61 | 462 | |
c95df587 YA |
463 | r = sd_dhcp_server_set_relay_target(link->dhcp_server, &link->network->dhcp_server_relay_target); |
464 | if (r < 0) | |
465 | return log_link_error_errno(link, r, "Failed to set relay target for DHCP server: %m"); | |
466 | ||
11c38d3e YA |
467 | bind_to_interface = sd_dhcp_server_is_in_relay_mode(link->dhcp_server) ? false : link->network->dhcp_server_bind_to_interface; |
468 | r = sd_dhcp_server_set_bind_to_interface(link->dhcp_server, bind_to_interface); | |
469 | if (r < 0) | |
470 | return log_link_error_errno(link, r, "Failed to set interface binding for DHCP server: %m"); | |
471 | ||
472 | r = sd_dhcp_server_set_relay_agent_information(link->dhcp_server, link->network->dhcp_server_relay_agent_circuit_id, link->network->dhcp_server_relay_agent_remote_id); | |
473 | if (r < 0) | |
474 | return log_link_error_errno(link, r, "Failed to set agent circuit/remote id for DHCP server: %m"); | |
475 | ||
8fcf1d61 YW |
476 | if (link->network->dhcp_server_emit_timezone) { |
477 | _cleanup_free_ char *buffer = NULL; | |
7b5018ca | 478 | const char *tz = NULL; |
8fcf1d61 YW |
479 | |
480 | if (link->network->dhcp_server_timezone) | |
481 | tz = link->network->dhcp_server_timezone; | |
482 | else { | |
483 | r = get_timezone(&buffer); | |
484 | if (r < 0) | |
7b5018ca | 485 | log_link_warning_errno(link, r, "Failed to determine timezone, not sending timezone: %m"); |
486 | else | |
487 | tz = buffer; | |
8fcf1d61 YW |
488 | } |
489 | ||
7b5018ca | 490 | if (tz) { |
491 | r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); | |
492 | if (r < 0) | |
493 | return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m"); | |
494 | } | |
8fcf1d61 | 495 | } |
564ca984 | 496 | |
90e74a66 | 497 | ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options) { |
461dbb2f | 498 | r = sd_dhcp_server_add_option(link->dhcp_server, p); |
564ca984 SS |
499 | if (r == -EEXIST) |
500 | continue; | |
501 | if (r < 0) | |
c00c3b64 | 502 | return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); |
564ca984 SS |
503 | } |
504 | ||
90e74a66 | 505 | ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options) { |
7354900d DW |
506 | r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p); |
507 | if (r == -EEXIST) | |
508 | continue; | |
509 | if (r < 0) | |
510 | return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); | |
511 | } | |
512 | ||
c517a49b | 513 | HASHMAP_FOREACH(static_lease, link->network->dhcp_static_leases_by_section) { |
514 | r = sd_dhcp_server_set_static_lease(link->dhcp_server, &static_lease->address, static_lease->client_id, static_lease->client_id_size); | |
515 | if (r < 0) | |
516 | return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m"); | |
517 | } | |
518 | ||
ab486ef4 YW |
519 | r = sd_dhcp_server_start(link->dhcp_server); |
520 | if (r < 0) | |
521 | return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m"); | |
5ae0fb7f | 522 | |
ab486ef4 | 523 | log_link_debug(link, "Offering DHCPv4 leases"); |
745f0620 | 524 | return 0; |
1d28a3cf YW |
525 | } |
526 | ||
1d28a3cf | 527 | static bool dhcp_server_is_ready_to_configure(Link *link) { |
165d7c5c | 528 | Link *uplink = NULL; |
1d28a3cf YW |
529 | Address *a; |
530 | ||
531 | assert(link); | |
5459e11d YW |
532 | assert(link->network); |
533 | assert(link->network->dhcp_server_address); | |
1d28a3cf | 534 | |
4b482e8b | 535 | if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false)) |
baa95d22 YW |
536 | return false; |
537 | ||
1d28a3cf YW |
538 | if (!link_has_carrier(link)) |
539 | return false; | |
540 | ||
1d28a3cf YW |
541 | if (!link->static_addresses_configured) |
542 | return false; | |
543 | ||
5459e11d | 544 | if (address_get(link, link->network->dhcp_server_address, &a) < 0) |
1d28a3cf YW |
545 | return false; |
546 | ||
547 | if (!address_is_ready(a)) | |
548 | return false; | |
549 | ||
165d7c5c YW |
550 | if (dhcp_server_find_uplink(link, &uplink) < 0) |
551 | return false; | |
552 | ||
553 | if (uplink && !uplink->network) | |
554 | return false; | |
555 | ||
1d28a3cf YW |
556 | return true; |
557 | } | |
558 | ||
09d09207 | 559 | static int dhcp_server_process_request(Request *req, Link *link, void *userdata) { |
745f0620 YW |
560 | int r; |
561 | ||
ff51134c | 562 | assert(link); |
1d28a3cf | 563 | |
745f0620 | 564 | if (!dhcp_server_is_ready_to_configure(link)) |
1d28a3cf YW |
565 | return 0; |
566 | ||
745f0620 YW |
567 | r = dhcp4_server_configure(link); |
568 | if (r < 0) | |
569 | return log_link_warning_errno(link, r, "Failed to configure DHCP server: %m"); | |
570 | ||
571 | return 1; | |
8fcf1d61 YW |
572 | } |
573 | ||
8bed7c55 | 574 | int link_request_dhcp_server(Link *link) { |
745f0620 YW |
575 | int r; |
576 | ||
8bed7c55 YW |
577 | assert(link); |
578 | ||
579 | if (!link_dhcp4_server_enabled(link)) | |
580 | return 0; | |
581 | ||
582 | if (link->dhcp_server) | |
583 | return 0; | |
584 | ||
585 | log_link_debug(link, "Requesting DHCP server."); | |
09d09207 | 586 | r = link_queue_request(link, REQUEST_TYPE_DHCP_SERVER, dhcp_server_process_request, NULL); |
745f0620 YW |
587 | if (r < 0) |
588 | return log_link_warning_errno(link, r, "Failed to request configuration of DHCP server: %m"); | |
589 | ||
590 | return 0; | |
8bed7c55 YW |
591 | } |
592 | ||
11c38d3e YA |
593 | int config_parse_dhcp_server_relay_agent_suboption( |
594 | const char *unit, | |
595 | const char *filename, | |
596 | unsigned line, | |
597 | const char *section, | |
598 | unsigned section_line, | |
599 | const char *lvalue, | |
600 | int ltype, | |
601 | const char *rvalue, | |
602 | void *data, | |
603 | void *userdata) { | |
604 | ||
605 | char **suboption_value = data; | |
606 | char* p; | |
607 | ||
608 | assert(filename); | |
609 | assert(lvalue); | |
610 | assert(rvalue); | |
611 | ||
11c38d3e YA |
612 | if (isempty(rvalue)) { |
613 | *suboption_value = mfree(*suboption_value); | |
614 | return 0; | |
615 | } | |
616 | ||
617 | p = startswith(rvalue, "string:"); | |
618 | if (!p) { | |
619 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
620 | "Failed to parse %s=%s'. Invalid format, ignoring.", lvalue, rvalue); | |
621 | return 0; | |
622 | } | |
623 | return free_and_strdup(suboption_value, empty_to_null(p)); | |
624 | } | |
625 | ||
2a71d57f | 626 | int config_parse_dhcp_server_emit( |
8fcf1d61 YW |
627 | const char *unit, |
628 | const char *filename, | |
629 | unsigned line, | |
2a71d57f LP |
630 | const char *section, |
631 | unsigned section_line, | |
8fcf1d61 | 632 | const char *lvalue, |
2a71d57f | 633 | int ltype, |
8fcf1d61 | 634 | const char *rvalue, |
2a71d57f LP |
635 | void *data, |
636 | void *userdata) { | |
8fcf1d61 | 637 | |
99534007 | 638 | NetworkDHCPServerEmitAddress *emit = ASSERT_PTR(data); |
2a71d57f | 639 | |
8fcf1d61 YW |
640 | assert(rvalue); |
641 | ||
faa1b3c6 YW |
642 | if (isempty(rvalue)) { |
643 | emit->addresses = mfree(emit->addresses); | |
644 | emit->n_addresses = 0; | |
645 | return 0; | |
646 | } | |
647 | ||
c1997a5b | 648 | for (const char *p = rvalue;;) { |
8fcf1d61 YW |
649 | _cleanup_free_ char *w = NULL; |
650 | union in_addr_union a; | |
c1997a5b | 651 | int r; |
8fcf1d61 YW |
652 | |
653 | r = extract_first_word(&p, &w, NULL, 0); | |
654 | if (r == -ENOMEM) | |
655 | return log_oom(); | |
656 | if (r < 0) { | |
d96edb2c | 657 | log_syntax(unit, LOG_WARNING, filename, line, r, |
8fcf1d61 YW |
658 | "Failed to extract word, ignoring: %s", rvalue); |
659 | return 0; | |
660 | } | |
661 | if (r == 0) | |
c1997a5b | 662 | return 0; |
8fcf1d61 | 663 | |
5f468b9f YW |
664 | if (streq(w, "_server_address")) |
665 | a = IN_ADDR_NULL; /* null address will be converted to the server address. */ | |
666 | else { | |
667 | r = in_addr_from_string(AF_INET, w, &a); | |
668 | if (r < 0) { | |
669 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
670 | "Failed to parse %s= address '%s', ignoring: %m", lvalue, w); | |
671 | continue; | |
672 | } | |
673 | ||
674 | if (in4_addr_is_null(&a.in)) { | |
675 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
676 | "Found a null address in %s=, ignoring.", lvalue); | |
677 | continue; | |
678 | } | |
8fcf1d61 YW |
679 | } |
680 | ||
77e73102 | 681 | if (!GREEDY_REALLOC(emit->addresses, emit->n_addresses + 1)) |
8fcf1d61 YW |
682 | return log_oom(); |
683 | ||
2a71d57f | 684 | emit->addresses[emit->n_addresses++] = a.in; |
8fcf1d61 | 685 | } |
8fcf1d61 | 686 | } |
0017ba31 YW |
687 | |
688 | int config_parse_dhcp_server_address( | |
689 | const char *unit, | |
690 | const char *filename, | |
691 | unsigned line, | |
692 | const char *section, | |
693 | unsigned section_line, | |
694 | const char *lvalue, | |
695 | int ltype, | |
696 | const char *rvalue, | |
697 | void *data, | |
698 | void *userdata) { | |
699 | ||
6278e428 | 700 | Network *network = ASSERT_PTR(userdata); |
0017ba31 YW |
701 | union in_addr_union a; |
702 | unsigned char prefixlen; | |
703 | int r; | |
704 | ||
705 | assert(filename); | |
706 | assert(lvalue); | |
707 | assert(rvalue); | |
708 | ||
709 | if (isempty(rvalue)) { | |
5459e11d | 710 | network->dhcp_server_address_in_addr = (struct in_addr) {}; |
0017ba31 YW |
711 | network->dhcp_server_address_prefixlen = 0; |
712 | return 0; | |
713 | } | |
714 | ||
715 | r = in_addr_prefix_from_string(rvalue, AF_INET, &a, &prefixlen); | |
716 | if (r < 0) { | |
717 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
718 | "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); | |
719 | return 0; | |
720 | } | |
721 | if (in4_addr_is_null(&a.in) || in4_addr_is_localhost(&a.in)) { | |
722 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
723 | "DHCP server address cannot be the ANY address or a localhost address, " | |
724 | "ignoring assignment: %s", rvalue); | |
725 | return 0; | |
726 | } | |
727 | ||
5459e11d | 728 | network->dhcp_server_address_in_addr = a.in; |
0017ba31 YW |
729 | network->dhcp_server_address_prefixlen = prefixlen; |
730 | return 0; | |
731 | } |