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