]>
Commit | Line | Data |
---|---|---|
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" | |
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 | int dhcp4_server_configure(Link *link) { | |
141 | Address *address; | |
142 | Link *uplink = NULL; | |
143 | bool acquired_uplink = false; | |
144 | int r; | |
145 | ||
146 | address = link_find_dhcp_server_address(link); | |
147 | if (!address) | |
148 | return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBUSY), | |
149 | "Failed to find suitable address for DHCPv4 server instance."); | |
150 | ||
151 | /* use the server address' subnet as the pool */ | |
152 | r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, | |
153 | link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); | |
154 | if (r < 0) | |
155 | return r; | |
156 | ||
157 | /* TODO: | |
158 | r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in); | |
159 | if (r < 0) | |
160 | return r; | |
161 | */ | |
162 | ||
163 | if (link->network->dhcp_server_max_lease_time_usec > 0) { | |
164 | r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, | |
165 | DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); | |
166 | if (r < 0) | |
167 | return r; | |
168 | } | |
169 | ||
170 | if (link->network->dhcp_server_default_lease_time_usec > 0) { | |
171 | r = sd_dhcp_server_set_default_lease_time(link->dhcp_server, | |
172 | DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); | |
173 | if (r < 0) | |
174 | return r; | |
175 | } | |
176 | ||
177 | if (link->network->dhcp_server_emit_dns) { | |
178 | if (link->network->n_dhcp_server_dns > 0) | |
179 | r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns); | |
180 | else { | |
181 | uplink = manager_find_uplink(link->manager, link); | |
182 | acquired_uplink = true; | |
183 | ||
184 | if (!uplink) { | |
185 | log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); | |
186 | r = 0; | |
187 | } else | |
188 | r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server); | |
189 | } | |
190 | if (r < 0) | |
191 | log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); | |
192 | } | |
193 | ||
194 | if (link->network->dhcp_server_emit_ntp) { | |
195 | if (link->network->n_dhcp_server_ntp > 0) | |
196 | r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp); | |
197 | else { | |
198 | if (!acquired_uplink) | |
199 | uplink = manager_find_uplink(link->manager, link); | |
200 | ||
201 | if (!uplink) { | |
202 | log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); | |
203 | r = 0; | |
204 | } else | |
205 | r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server); | |
206 | ||
207 | } | |
208 | if (r < 0) | |
209 | log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); | |
210 | } | |
211 | ||
212 | r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); | |
213 | if (r < 0) | |
214 | return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m"); | |
215 | ||
216 | if (link->network->dhcp_server_emit_timezone) { | |
217 | _cleanup_free_ char *buffer = NULL; | |
218 | const char *tz; | |
219 | ||
220 | if (link->network->dhcp_server_timezone) | |
221 | tz = link->network->dhcp_server_timezone; | |
222 | else { | |
223 | r = get_timezone(&buffer); | |
224 | if (r < 0) | |
225 | return log_warning_errno(r, "Failed to determine timezone: %m"); | |
226 | ||
227 | tz = buffer; | |
228 | } | |
229 | ||
230 | r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); | |
231 | if (r < 0) | |
232 | return r; | |
233 | } | |
234 | if (!sd_dhcp_server_is_running(link->dhcp_server)) { | |
235 | r = sd_dhcp_server_start(link->dhcp_server); | |
236 | if (r < 0) | |
237 | return log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m"); | |
238 | } | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | int config_parse_dhcp_server_dns( | |
244 | const char *unit, | |
245 | const char *filename, | |
246 | unsigned line, | |
247 | const char *section, | |
248 | unsigned section_line, | |
249 | const char *lvalue, | |
250 | int ltype, | |
251 | const char *rvalue, | |
252 | void *data, | |
253 | void *userdata) { | |
254 | ||
255 | Network *n = data; | |
256 | const char *p = rvalue; | |
257 | int r; | |
258 | ||
259 | assert(filename); | |
260 | assert(lvalue); | |
261 | assert(rvalue); | |
262 | ||
263 | for (;;) { | |
264 | _cleanup_free_ char *w = NULL; | |
265 | union in_addr_union a; | |
266 | struct in_addr *m; | |
267 | ||
268 | r = extract_first_word(&p, &w, NULL, 0); | |
269 | if (r == -ENOMEM) | |
270 | return log_oom(); | |
271 | if (r < 0) { | |
272 | log_syntax(unit, LOG_ERR, filename, line, r, | |
273 | "Failed to extract word, ignoring: %s", rvalue); | |
274 | return 0; | |
275 | } | |
276 | if (r == 0) | |
277 | break; | |
278 | ||
279 | r = in_addr_from_string(AF_INET, w, &a); | |
280 | if (r < 0) { | |
281 | log_syntax(unit, LOG_ERR, filename, line, r, | |
282 | "Failed to parse DNS server address '%s', ignoring assignment: %m", w); | |
283 | continue; | |
284 | } | |
285 | ||
286 | m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr)); | |
287 | if (!m) | |
288 | return log_oom(); | |
289 | ||
290 | m[n->n_dhcp_server_dns++] = a.in; | |
291 | n->dhcp_server_dns = m; | |
292 | } | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | int config_parse_dhcp_server_ntp( | |
298 | const char *unit, | |
299 | const char *filename, | |
300 | unsigned line, | |
301 | const char *section, | |
302 | unsigned section_line, | |
303 | const char *lvalue, | |
304 | int ltype, | |
305 | const char *rvalue, | |
306 | void *data, | |
307 | void *userdata) { | |
308 | ||
309 | Network *n = data; | |
310 | const char *p = rvalue; | |
311 | int r; | |
312 | ||
313 | assert(filename); | |
314 | assert(lvalue); | |
315 | assert(rvalue); | |
316 | ||
317 | for (;;) { | |
318 | _cleanup_free_ char *w = NULL; | |
319 | union in_addr_union a; | |
320 | struct in_addr *m; | |
321 | ||
322 | r = extract_first_word(&p, &w, NULL, 0); | |
323 | if (r == -ENOMEM) | |
324 | return log_oom(); | |
325 | if (r < 0) { | |
326 | log_syntax(unit, LOG_ERR, filename, line, r, | |
327 | "Failed to extract word, ignoring: %s", rvalue); | |
328 | return 0; | |
329 | } | |
330 | if (r == 0) | |
331 | return 0; | |
332 | ||
333 | r = in_addr_from_string(AF_INET, w, &a); | |
334 | if (r < 0) { | |
335 | log_syntax(unit, LOG_ERR, filename, line, r, | |
336 | "Failed to parse NTP server address '%s', ignoring: %m", w); | |
337 | continue; | |
338 | } | |
339 | ||
340 | m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr)); | |
341 | if (!m) | |
342 | return log_oom(); | |
343 | ||
344 | m[n->n_dhcp_server_ntp++] = a.in; | |
345 | n->dhcp_server_ntp = m; | |
346 | } | |
347 | } |