]>
Commit | Line | Data |
---|---|---|
a6cc569e TG |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright (C) 2013 Intel Corporation. All rights reserved. | |
5 | Copyright (C) 2014 Tom Gundersen | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
21 | #include <stdlib.h> | |
22 | #include <errno.h> | |
23 | #include <string.h> | |
24 | #include <stdio.h> | |
25 | #include <net/ethernet.h> | |
fe8db0c5 | 26 | #include <arpa/inet.h> |
a6cc569e TG |
27 | #include <sys/param.h> |
28 | ||
29 | #include "util.h" | |
30 | #include "list.h" | |
fe8db0c5 TG |
31 | #include "mkdir.h" |
32 | #include "fileio.h" | |
df40eee8 | 33 | #include "in-addr-util.h" |
a6cc569e TG |
34 | |
35 | #include "dhcp-protocol.h" | |
36 | #include "dhcp-internal.h" | |
fe8db0c5 TG |
37 | #include "dhcp-lease-internal.h" |
38 | #include "sd-dhcp-lease.h" | |
a6cc569e | 39 | #include "sd-dhcp-client.h" |
09bee74d | 40 | #include "network-internal.h" |
a6cc569e TG |
41 | |
42 | int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { | |
43 | assert_return(lease, -EINVAL); | |
44 | assert_return(addr, -EINVAL); | |
45 | ||
46 | addr->s_addr = lease->address; | |
47 | ||
48 | return 0; | |
49 | } | |
50 | ||
68ceb9df PF |
51 | int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { |
52 | assert_return(lease, -EINVAL); | |
53 | assert_return(lease, -EINVAL); | |
54 | ||
55 | *lifetime = lease->lifetime; | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
a6cc569e TG |
60 | int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { |
61 | assert_return(lease, -EINVAL); | |
62 | assert_return(mtu, -EINVAL); | |
63 | ||
64 | if (lease->mtu) | |
65 | *mtu = lease->mtu; | |
66 | else | |
67 | return -ENOENT; | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
a2ba62c7 | 72 | int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { |
a6cc569e TG |
73 | assert_return(lease, -EINVAL); |
74 | assert_return(addr, -EINVAL); | |
a6cc569e TG |
75 | |
76 | if (lease->dns_size) { | |
a6cc569e | 77 | *addr = lease->dns; |
a2ba62c7 | 78 | return lease->dns_size; |
a6cc569e TG |
79 | } else |
80 | return -ENOENT; | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
a2ba62c7 | 85 | int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { |
46844696 TG |
86 | assert_return(lease, -EINVAL); |
87 | assert_return(addr, -EINVAL); | |
46844696 TG |
88 | |
89 | if (lease->ntp_size) { | |
46844696 | 90 | *addr = lease->ntp; |
a2ba62c7 | 91 | return lease->ntp_size; |
46844696 TG |
92 | } else |
93 | return -ENOENT; | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
a6cc569e TG |
98 | int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { |
99 | assert_return(lease, -EINVAL); | |
100 | assert_return(domainname, -EINVAL); | |
101 | ||
102 | if (lease->domainname) | |
103 | *domainname = lease->domainname; | |
104 | else | |
105 | return -ENOENT; | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) { | |
111 | assert_return(lease, -EINVAL); | |
112 | assert_return(hostname, -EINVAL); | |
113 | ||
114 | if (lease->hostname) | |
115 | *hostname = lease->hostname; | |
116 | else | |
117 | return -ENOENT; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
ce78df79 TG |
122 | int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { |
123 | assert_return(lease, -EINVAL); | |
124 | assert_return(root_path, -EINVAL); | |
125 | ||
126 | if (lease->root_path) | |
127 | *root_path = lease->root_path; | |
128 | else | |
129 | return -ENOENT; | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
a6cc569e TG |
134 | int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { |
135 | assert_return(lease, -EINVAL); | |
136 | assert_return(addr, -EINVAL); | |
137 | ||
8ddbeaa2 UTL |
138 | if (lease->router != INADDR_ANY) |
139 | addr->s_addr = lease->router; | |
140 | else | |
141 | return -ENOENT; | |
a6cc569e TG |
142 | |
143 | return 0; | |
144 | } | |
145 | ||
146 | int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { | |
147 | assert_return(lease, -EINVAL); | |
148 | assert_return(addr, -EINVAL); | |
149 | ||
150 | addr->s_addr = lease->subnet_mask; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
0ad853bc TG |
155 | int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) { |
156 | assert_return(lease, -EINVAL); | |
157 | assert_return(addr, -EINVAL); | |
158 | ||
159 | addr->s_addr = lease->server_address; | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
8e34a618 TG |
164 | int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { |
165 | assert_return(lease, -EINVAL); | |
166 | assert_return(addr, -EINVAL); | |
167 | ||
168 | addr->s_addr = lease->next_server; | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
a2ba62c7 | 173 | int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) { |
e1ea665e EY |
174 | |
175 | assert_return(lease, -EINVAL); | |
176 | assert_return(routes, -EINVAL); | |
e1ea665e EY |
177 | |
178 | if (lease->static_route_size) { | |
179 | *routes = lease->static_route; | |
a2ba62c7 | 180 | return lease->static_route_size; |
e1ea665e EY |
181 | } else |
182 | return -ENOENT; | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
a6cc569e TG |
187 | sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { |
188 | if (lease) | |
189 | assert_se(REFCNT_INC(lease->n_ref) >= 2); | |
190 | ||
191 | return lease; | |
192 | } | |
193 | ||
194 | sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { | |
195 | if (lease && REFCNT_DEC(lease->n_ref) <= 0) { | |
196 | free(lease->hostname); | |
197 | free(lease->domainname); | |
198 | free(lease->dns); | |
81d98a39 | 199 | free(lease->ntp); |
e1ea665e | 200 | free(lease->static_route); |
a6cc569e TG |
201 | free(lease); |
202 | } | |
203 | ||
204 | return NULL; | |
205 | } | |
206 | ||
e140ae58 TG |
207 | static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { |
208 | be32_t val; | |
209 | ||
210 | assert(option); | |
211 | assert(ret); | |
212 | ||
213 | if (len == 4) { | |
214 | memcpy(&val, option, 4); | |
215 | *ret = be32toh(val); | |
216 | ||
217 | if (*ret < min) | |
218 | *ret = min; | |
219 | } | |
220 | } | |
221 | ||
f5c0c00f TG |
222 | static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) { |
223 | lease_parse_u32(option, len, (uint32_t *)ret, 0); | |
224 | } | |
225 | ||
e140ae58 TG |
226 | static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { |
227 | be16_t val; | |
228 | ||
229 | assert(option); | |
230 | assert(ret); | |
231 | ||
232 | if (len == 2) { | |
233 | memcpy(&val, option, 2); | |
234 | *ret = be16toh(val); | |
235 | ||
236 | if (*ret < min) | |
237 | *ret = min; | |
238 | } | |
239 | } | |
240 | ||
241 | static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) { | |
242 | assert(option); | |
243 | assert(ret); | |
244 | ||
245 | if (len == 4) | |
246 | memcpy(ret, option, 4); | |
247 | } | |
248 | ||
f5c0c00f TG |
249 | static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) { |
250 | assert(option); | |
251 | assert(ret); | |
252 | ||
253 | if (len == 1) | |
254 | *ret = !!(*option); | |
255 | } | |
256 | ||
257 | static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) { | |
258 | assert(option); | |
259 | assert(ret); | |
260 | ||
261 | if (len == 1) { | |
262 | *ret = *option; | |
263 | ||
264 | if (*ret < min) | |
265 | *ret = min; | |
266 | } | |
267 | } | |
268 | ||
e140ae58 TG |
269 | static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { |
270 | assert(option); | |
271 | assert(ret); | |
272 | ||
273 | if (len >= 1) { | |
274 | char *string; | |
275 | ||
276 | string = strndup((const char *)option, len); | |
277 | if (!string) | |
278 | return -errno; | |
279 | ||
280 | free(*ret); | |
281 | *ret = string; | |
282 | } | |
283 | ||
284 | return 0; | |
285 | } | |
286 | ||
f5c0c00f | 287 | static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) { |
e140ae58 TG |
288 | assert(option); |
289 | assert(ret); | |
290 | assert(ret_size); | |
291 | ||
f5c0c00f | 292 | if (len && !(len % (4 * mult))) { |
e140ae58 TG |
293 | size_t size; |
294 | struct in_addr *addresses; | |
295 | ||
296 | size = len / 4; | |
297 | ||
298 | addresses = newdup(struct in_addr, option, size); | |
299 | if (!addresses) | |
300 | return -ENOMEM; | |
301 | ||
302 | free(*ret); | |
303 | *ret = addresses; | |
304 | *ret_size = size; | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
f5c0c00f TG |
310 | static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) { |
311 | return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1); | |
312 | } | |
313 | ||
314 | static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) { | |
315 | return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2); | |
316 | } | |
317 | ||
e1ea665e EY |
318 | static int class_prefixlen(uint8_t msb_octet, uint8_t *ret) { |
319 | if (msb_octet < 128) | |
320 | /* Class A */ | |
321 | *ret = 8; | |
322 | else if (msb_octet < 192) | |
323 | /* Class B */ | |
324 | *ret = 16; | |
325 | else if (msb_octet < 224) | |
326 | /* Class C */ | |
327 | *ret = 24; | |
328 | else | |
329 | /* Class D or E -- no subnet mask */ | |
330 | return -ERANGE; | |
331 | ||
332 | return 0; | |
333 | } | |
334 | ||
335 | static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes, | |
336 | size_t *routes_size, size_t *routes_allocated) { | |
337 | ||
338 | struct in_addr addr; | |
339 | ||
340 | assert(option); | |
341 | assert(routes); | |
342 | assert(routes_size); | |
343 | assert(routes_allocated); | |
344 | ||
345 | if (!len) | |
346 | return 0; | |
347 | ||
348 | if (len % 8 != 0) | |
349 | return -EINVAL; | |
350 | ||
351 | if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8))) | |
352 | return -ENOMEM; | |
353 | ||
354 | while (len >= 8) { | |
355 | struct sd_dhcp_route *route = *routes + *routes_size; | |
356 | ||
357 | if (class_prefixlen(*option, &route->dst_prefixlen) < 0) { | |
358 | log_error("Failed to determine destination prefix length from class based IP, ignoring"); | |
359 | continue; | |
360 | } | |
361 | ||
362 | lease_parse_be32(option, 4, &addr.s_addr); | |
363 | route->dst_addr = inet_makeaddr(inet_netof(addr), 0); | |
364 | option += 4; | |
365 | ||
366 | lease_parse_be32(option, 4, &route->gw_addr.s_addr); | |
367 | option += 4; | |
368 | ||
369 | len -= 8; | |
370 | (*routes_size)++; | |
371 | } | |
372 | ||
373 | return 0; | |
374 | } | |
375 | ||
376 | /* parses RFC3442 Classless Static Route Option */ | |
377 | static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes, | |
378 | size_t *routes_size, size_t *routes_allocated) { | |
379 | ||
380 | assert(option); | |
381 | assert(routes); | |
382 | assert(routes_size); | |
383 | assert(routes_allocated); | |
384 | ||
385 | /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ | |
386 | ||
387 | while (len > 0) { | |
388 | uint8_t dst_octets; | |
389 | struct sd_dhcp_route *route; | |
390 | ||
391 | if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1)) | |
392 | return -ENOMEM; | |
393 | ||
394 | route = *routes + *routes_size; | |
395 | ||
396 | dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); | |
397 | route->dst_prefixlen = *option; | |
398 | option++; | |
399 | len--; | |
400 | ||
401 | /* can't have more than 4 octets in IPv4 */ | |
402 | if (dst_octets > 4 || len < dst_octets) | |
403 | return -EINVAL; | |
404 | ||
405 | route->dst_addr.s_addr = 0; | |
406 | memcpy(&route->dst_addr.s_addr, option, dst_octets); | |
407 | option += dst_octets; | |
408 | len -= dst_octets; | |
409 | ||
410 | if (len < 4) | |
411 | return -EINVAL; | |
412 | ||
413 | lease_parse_be32(option, 4, &route->gw_addr.s_addr); | |
414 | option += 4; | |
415 | len -= 4; | |
416 | ||
417 | (*routes_size)++; | |
418 | } | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
a6cc569e TG |
423 | int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, |
424 | void *user_data) { | |
425 | sd_dhcp_lease *lease = user_data; | |
e140ae58 TG |
426 | int r; |
427 | ||
428 | assert(lease); | |
a6cc569e TG |
429 | |
430 | switch(code) { | |
431 | ||
f5c0c00f TG |
432 | case DHCP_OPTION_TIME_OFFSET: |
433 | lease_parse_s32(option, len, &lease->time_offset); | |
434 | ||
435 | break; | |
436 | ||
437 | case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT: | |
438 | lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0); | |
439 | ||
440 | break; | |
441 | ||
a6cc569e | 442 | case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: |
e140ae58 | 443 | lease_parse_u32(option, len, &lease->lifetime, 1); |
a6cc569e TG |
444 | |
445 | break; | |
446 | ||
447 | case DHCP_OPTION_SERVER_IDENTIFIER: | |
e140ae58 | 448 | lease_parse_be32(option, len, &lease->server_address); |
a6cc569e TG |
449 | |
450 | break; | |
451 | ||
452 | case DHCP_OPTION_SUBNET_MASK: | |
e140ae58 | 453 | lease_parse_be32(option, len, &lease->subnet_mask); |
a6cc569e TG |
454 | |
455 | break; | |
456 | ||
f5c0c00f TG |
457 | case DHCP_OPTION_BROADCAST: |
458 | lease_parse_be32(option, len, &lease->broadcast); | |
459 | ||
460 | break; | |
461 | ||
a6cc569e | 462 | case DHCP_OPTION_ROUTER: |
e140ae58 | 463 | lease_parse_be32(option, len, &lease->router); |
a6cc569e TG |
464 | |
465 | break; | |
466 | ||
467 | case DHCP_OPTION_DOMAIN_NAME_SERVER: | |
e140ae58 TG |
468 | r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size); |
469 | if (r < 0) | |
470 | return r; | |
46844696 TG |
471 | |
472 | break; | |
473 | ||
474 | case DHCP_OPTION_NTP_SERVER: | |
e140ae58 TG |
475 | r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size); |
476 | if (r < 0) | |
477 | return r; | |
a6cc569e TG |
478 | |
479 | break; | |
480 | ||
f5c0c00f TG |
481 | case DHCP_OPTION_POLICY_FILTER: |
482 | r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size); | |
483 | if (r < 0) | |
484 | return r; | |
485 | ||
486 | break; | |
487 | ||
488 | case DHCP_OPTION_STATIC_ROUTE: | |
e1ea665e EY |
489 | r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, |
490 | &lease->static_route_allocated); | |
f5c0c00f TG |
491 | if (r < 0) |
492 | return r; | |
493 | ||
494 | break; | |
495 | ||
a6cc569e | 496 | case DHCP_OPTION_INTERFACE_MTU: |
e140ae58 | 497 | lease_parse_u16(option, len, &lease->mtu, 68); |
a6cc569e TG |
498 | |
499 | break; | |
500 | ||
f5c0c00f TG |
501 | case DHCP_OPTION_INTERFACE_MDR: |
502 | lease_parse_u16(option, len, &lease->mdr, 576); | |
503 | ||
504 | break; | |
505 | ||
506 | case DHCP_OPTION_INTERFACE_TTL: | |
507 | lease_parse_u8(option, len, &lease->ttl, 1); | |
508 | ||
509 | break; | |
510 | ||
511 | case DHCP_OPTION_BOOT_FILE_SIZE: | |
512 | lease_parse_u16(option, len, &lease->boot_file_size, 0); | |
513 | ||
514 | break; | |
515 | ||
a6cc569e | 516 | case DHCP_OPTION_DOMAIN_NAME: |
784d9b9c TG |
517 | { |
518 | _cleanup_free_ char *domainname = NULL; | |
519 | ||
520 | r = lease_parse_string(option, len, &domainname); | |
e140ae58 TG |
521 | if (r < 0) |
522 | return r; | |
a6cc569e | 523 | |
784d9b9c TG |
524 | if (!hostname_is_valid(domainname) || is_localhost(domainname)) |
525 | break; | |
526 | ||
527 | free(lease->domainname); | |
528 | lease->domainname = domainname; | |
529 | domainname = NULL; | |
a6cc569e | 530 | |
784d9b9c TG |
531 | break; |
532 | } | |
a6cc569e | 533 | case DHCP_OPTION_HOST_NAME: |
784d9b9c TG |
534 | { |
535 | _cleanup_free_ char *hostname = NULL; | |
536 | ||
537 | r = lease_parse_string(option, len, &hostname); | |
e140ae58 TG |
538 | if (r < 0) |
539 | return r; | |
a6cc569e | 540 | |
708281b8 | 541 | if (!hostname_is_valid(hostname) || is_localhost(hostname)) |
784d9b9c TG |
542 | break; |
543 | ||
544 | free(lease->hostname); | |
545 | lease->hostname = hostname; | |
546 | hostname = NULL; | |
a6cc569e | 547 | |
784d9b9c TG |
548 | break; |
549 | } | |
ce78df79 | 550 | case DHCP_OPTION_ROOT_PATH: |
e140ae58 TG |
551 | r = lease_parse_string(option, len, &lease->root_path); |
552 | if (r < 0) | |
553 | return r; | |
ce78df79 TG |
554 | |
555 | break; | |
556 | ||
a6cc569e | 557 | case DHCP_OPTION_RENEWAL_T1_TIME: |
e140ae58 | 558 | lease_parse_u32(option, len, &lease->t1, 1); |
a6cc569e TG |
559 | |
560 | break; | |
561 | ||
562 | case DHCP_OPTION_REBINDING_T2_TIME: | |
e140ae58 | 563 | lease_parse_u32(option, len, &lease->t2, 1); |
a6cc569e | 564 | |
f5c0c00f TG |
565 | break; |
566 | ||
567 | case DHCP_OPTION_ENABLE_IP_FORWARDING: | |
568 | lease_parse_bool(option, len, &lease->ip_forward); | |
569 | ||
570 | break; | |
571 | ||
572 | case DHCP_OPTION_ENABLE_IP_FORWARDING_NL: | |
573 | lease_parse_bool(option, len, &lease->ip_forward_non_local); | |
574 | ||
a6cc569e | 575 | break; |
e1ea665e EY |
576 | |
577 | case DHCP_OPTION_CLASSLESS_STATIC_ROUTE: | |
578 | r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size, | |
579 | &lease->static_route_allocated); | |
580 | if (r < 0) | |
581 | return r; | |
582 | ||
583 | break; | |
a6cc569e TG |
584 | } |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
589 | int dhcp_lease_new(sd_dhcp_lease **ret) { | |
6e00a806 | 590 | sd_dhcp_lease *lease; |
a6cc569e TG |
591 | |
592 | lease = new0(sd_dhcp_lease, 1); | |
593 | if (!lease) | |
594 | return -ENOMEM; | |
595 | ||
8ddbeaa2 | 596 | lease->router = INADDR_ANY; |
a6cc569e TG |
597 | lease->n_ref = REFCNT_INIT; |
598 | ||
599 | *ret = lease; | |
a6cc569e TG |
600 | return 0; |
601 | } | |
fe8db0c5 TG |
602 | |
603 | int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { | |
604 | _cleanup_free_ char *temp_path = NULL; | |
605 | _cleanup_fclose_ FILE *f = NULL; | |
fe8db0c5 | 606 | struct in_addr address; |
a2ba62c7 | 607 | const struct in_addr *addresses; |
fe8db0c5 TG |
608 | const char *string; |
609 | uint16_t mtu; | |
e1ea665e | 610 | struct sd_dhcp_route *routes; |
fe8db0c5 TG |
611 | int r; |
612 | ||
613 | assert(lease); | |
614 | assert(lease_file); | |
615 | ||
fe8db0c5 TG |
616 | r = fopen_temporary(lease_file, &f, &temp_path); |
617 | if (r < 0) | |
618 | goto finish; | |
619 | ||
620 | fchmod(fileno(f), 0644); | |
621 | ||
622 | r = sd_dhcp_lease_get_address(lease, &address); | |
623 | if (r < 0) | |
624 | goto finish; | |
625 | ||
fe8db0c5 TG |
626 | fprintf(f, |
627 | "# This is private data. Do not parse.\n" | |
109731eb | 628 | "ADDRESS=%s\n", inet_ntoa(address)); |
fe8db0c5 | 629 | |
fe8db0c5 TG |
630 | r = sd_dhcp_lease_get_netmask(lease, &address); |
631 | if (r < 0) | |
632 | goto finish; | |
633 | ||
109731eb | 634 | fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); |
fe8db0c5 | 635 | |
8ddbeaa2 UTL |
636 | r = sd_dhcp_lease_get_router(lease, &address); |
637 | if (r >= 0) | |
638 | fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); | |
639 | ||
0ad853bc | 640 | r = sd_dhcp_lease_get_server_identifier(lease, &address); |
109731eb TG |
641 | if (r >= 0) |
642 | fprintf(f, "SERVER_ADDRESS=%s\n", | |
643 | inet_ntoa(address)); | |
0ad853bc | 644 | |
8e34a618 | 645 | r = sd_dhcp_lease_get_next_server(lease, &address); |
109731eb TG |
646 | if (r >= 0) |
647 | fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); | |
8e34a618 | 648 | |
fe8db0c5 TG |
649 | r = sd_dhcp_lease_get_mtu(lease, &mtu); |
650 | if (r >= 0) | |
651 | fprintf(f, "MTU=%" PRIu16 "\n", mtu); | |
652 | ||
b0e39c82 | 653 | fputs("DNS=", f); |
a2ba62c7 | 654 | r = sd_dhcp_lease_get_dns(lease, &addresses); |
109731eb | 655 | if (r >= 0) |
b0e39c82 TG |
656 | serialize_in_addrs(f, addresses, r); |
657 | fputs("\n", f); | |
109731eb | 658 | |
b0e39c82 | 659 | fputs("NTP=", f); |
a2ba62c7 | 660 | r = sd_dhcp_lease_get_ntp(lease, &addresses); |
109731eb | 661 | if (r >= 0) |
b0e39c82 TG |
662 | serialize_in_addrs(f, addresses, r); |
663 | fputs("\n", f); | |
fe8db0c5 TG |
664 | |
665 | r = sd_dhcp_lease_get_domainname(lease, &string); | |
666 | if (r >= 0) | |
667 | fprintf(f, "DOMAINNAME=%s\n", string); | |
668 | ||
669 | r = sd_dhcp_lease_get_hostname(lease, &string); | |
670 | if (r >= 0) | |
671 | fprintf(f, "HOSTNAME=%s\n", string); | |
672 | ||
ce78df79 TG |
673 | r = sd_dhcp_lease_get_root_path(lease, &string); |
674 | if (r >= 0) | |
675 | fprintf(f, "ROOT_PATH=%s\n", string); | |
676 | ||
a2ba62c7 | 677 | r = sd_dhcp_lease_get_routes(lease, &routes); |
e1ea665e | 678 | if (r >= 0) |
a2ba62c7 | 679 | serialize_dhcp_routes(f, "ROUTES", routes, r); |
e1ea665e | 680 | |
fe8db0c5 TG |
681 | r = 0; |
682 | ||
683 | fflush(f); | |
684 | ||
685 | if (ferror(f) || rename(temp_path, lease_file) < 0) { | |
686 | r = -errno; | |
687 | unlink(lease_file); | |
688 | unlink(temp_path); | |
689 | } | |
690 | ||
691 | finish: | |
692 | if (r < 0) | |
693 | log_error("Failed to save lease data %s: %s", lease_file, strerror(-r)); | |
694 | ||
695 | return r; | |
696 | } | |
697 | ||
698 | int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) { | |
699 | _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; | |
700 | _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL, | |
8e34a618 | 701 | *server_address = NULL, *next_server = NULL, |
e1ea665e | 702 | *dns = NULL, *ntp = NULL, *mtu = NULL, *routes = NULL; |
fe8db0c5 TG |
703 | struct in_addr addr; |
704 | int r; | |
705 | ||
706 | assert(lease_file); | |
707 | assert(ret); | |
708 | ||
709 | r = dhcp_lease_new(&lease); | |
710 | if (r < 0) | |
711 | return r; | |
712 | ||
713 | r = parse_env_file(lease_file, NEWLINE, | |
714 | "ADDRESS", &address, | |
715 | "ROUTER", &router, | |
716 | "NETMASK", &netmask, | |
0ad853bc | 717 | "SERVER_IDENTIFIER", &server_address, |
8e34a618 | 718 | "NEXT_SERVER", &next_server, |
109731eb TG |
719 | "DNS", &dns, |
720 | "NTP", &ntp, | |
fe8db0c5 TG |
721 | "MTU", &mtu, |
722 | "DOMAINNAME", &lease->domainname, | |
723 | "HOSTNAME", &lease->hostname, | |
ce78df79 | 724 | "ROOT_PATH", &lease->root_path, |
e1ea665e | 725 | "ROUTES", &routes, |
fe8db0c5 TG |
726 | NULL); |
727 | if (r < 0) { | |
728 | if (r == -ENOENT) | |
729 | return 0; | |
730 | ||
731 | log_error("Failed to read %s: %s", lease_file, strerror(-r)); | |
732 | return r; | |
733 | } | |
734 | ||
735 | r = inet_pton(AF_INET, address, &addr); | |
736 | if (r < 0) | |
737 | return r; | |
738 | ||
739 | lease->address = addr.s_addr; | |
740 | ||
8ddbeaa2 UTL |
741 | if (router) { |
742 | r = inet_pton(AF_INET, router, &addr); | |
743 | if (r < 0) | |
744 | return r; | |
fe8db0c5 | 745 | |
8ddbeaa2 UTL |
746 | lease->router = addr.s_addr; |
747 | } | |
fe8db0c5 TG |
748 | |
749 | r = inet_pton(AF_INET, netmask, &addr); | |
750 | if (r < 0) | |
751 | return r; | |
752 | ||
753 | lease->subnet_mask = addr.s_addr; | |
754 | ||
0ad853bc TG |
755 | if (server_address) { |
756 | r = inet_pton(AF_INET, server_address, &addr); | |
757 | if (r < 0) | |
758 | return r; | |
759 | ||
760 | lease->server_address = addr.s_addr; | |
761 | } | |
762 | ||
8e34a618 TG |
763 | if (next_server) { |
764 | r = inet_pton(AF_INET, next_server, &addr); | |
765 | if (r < 0) | |
766 | return r; | |
767 | ||
768 | lease->next_server = addr.s_addr; | |
769 | } | |
770 | ||
109731eb | 771 | if (dns) { |
a2ba62c7 | 772 | r = deserialize_in_addrs(&lease->dns, dns); |
109731eb TG |
773 | if (r < 0) |
774 | return r; | |
a2ba62c7 LP |
775 | |
776 | lease->dns_size = r; | |
109731eb TG |
777 | } |
778 | ||
779 | if (ntp) { | |
a2ba62c7 | 780 | r = deserialize_in_addrs(&lease->ntp, ntp); |
109731eb TG |
781 | if (r < 0) |
782 | return r; | |
a2ba62c7 LP |
783 | |
784 | lease->ntp_size = r; | |
109731eb TG |
785 | } |
786 | ||
fe8db0c5 TG |
787 | if (mtu) { |
788 | uint16_t u; | |
789 | if (sscanf(mtu, "%" SCNu16, &u) > 0) | |
790 | lease->mtu = u; | |
791 | } | |
792 | ||
e1ea665e EY |
793 | if (routes) { |
794 | r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size, | |
795 | &lease->static_route_allocated, routes); | |
796 | if (r < 0) | |
797 | return r; | |
798 | } | |
799 | ||
fe8db0c5 TG |
800 | *ret = lease; |
801 | lease = NULL; | |
802 | ||
803 | return 0; | |
804 | } | |
9e64dd72 TG |
805 | |
806 | int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { | |
df40eee8 TG |
807 | struct in_addr address; |
808 | struct in_addr mask; | |
809 | int r; | |
9e64dd72 TG |
810 | |
811 | assert(lease); | |
9e64dd72 | 812 | |
df40eee8 | 813 | address.s_addr = lease->address; |
9e64dd72 TG |
814 | |
815 | /* fall back to the default subnet masks based on address class */ | |
df40eee8 TG |
816 | r = in_addr_default_subnet_mask(&address, &mask); |
817 | if (r < 0) | |
818 | return r; | |
9e64dd72 | 819 | |
df40eee8 | 820 | lease->subnet_mask = mask.s_addr; |
9e64dd72 TG |
821 | |
822 | return 0; | |
823 | } |