]>
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 TG |
38 | #include "sd-dhcp-client.h" |
39 | ||
40 | int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { | |
41 | assert_return(lease, -EINVAL); | |
42 | assert_return(addr, -EINVAL); | |
43 | ||
44 | addr->s_addr = lease->address; | |
45 | ||
46 | return 0; | |
47 | } | |
48 | ||
49 | int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { | |
50 | assert_return(lease, -EINVAL); | |
51 | assert_return(mtu, -EINVAL); | |
52 | ||
53 | if (lease->mtu) | |
54 | *mtu = lease->mtu; | |
55 | else | |
56 | return -ENOENT; | |
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
61 | int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) { | |
62 | assert_return(lease, -EINVAL); | |
63 | assert_return(addr, -EINVAL); | |
64 | assert_return(addr_size, -EINVAL); | |
65 | ||
66 | if (lease->dns_size) { | |
67 | *addr_size = lease->dns_size; | |
68 | *addr = lease->dns; | |
69 | } else | |
70 | return -ENOENT; | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
46844696 TG |
75 | int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) { |
76 | assert_return(lease, -EINVAL); | |
77 | assert_return(addr, -EINVAL); | |
78 | assert_return(addr_size, -EINVAL); | |
79 | ||
80 | if (lease->ntp_size) { | |
81 | *addr_size = lease->ntp_size; | |
82 | *addr = lease->ntp; | |
83 | } else | |
84 | return -ENOENT; | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
a6cc569e TG |
89 | int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { |
90 | assert_return(lease, -EINVAL); | |
91 | assert_return(domainname, -EINVAL); | |
92 | ||
93 | if (lease->domainname) | |
94 | *domainname = lease->domainname; | |
95 | else | |
96 | return -ENOENT; | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) { | |
102 | assert_return(lease, -EINVAL); | |
103 | assert_return(hostname, -EINVAL); | |
104 | ||
105 | if (lease->hostname) | |
106 | *hostname = lease->hostname; | |
107 | else | |
108 | return -ENOENT; | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
ce78df79 TG |
113 | int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { |
114 | assert_return(lease, -EINVAL); | |
115 | assert_return(root_path, -EINVAL); | |
116 | ||
117 | if (lease->root_path) | |
118 | *root_path = lease->root_path; | |
119 | else | |
120 | return -ENOENT; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
a6cc569e TG |
125 | int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { |
126 | assert_return(lease, -EINVAL); | |
127 | assert_return(addr, -EINVAL); | |
128 | ||
8ddbeaa2 UTL |
129 | if (lease->router != INADDR_ANY) |
130 | addr->s_addr = lease->router; | |
131 | else | |
132 | return -ENOENT; | |
a6cc569e TG |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { | |
138 | assert_return(lease, -EINVAL); | |
139 | assert_return(addr, -EINVAL); | |
140 | ||
141 | addr->s_addr = lease->subnet_mask; | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
0ad853bc TG |
146 | int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) { |
147 | assert_return(lease, -EINVAL); | |
148 | assert_return(addr, -EINVAL); | |
149 | ||
150 | addr->s_addr = lease->server_address; | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
8e34a618 TG |
155 | int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { |
156 | assert_return(lease, -EINVAL); | |
157 | assert_return(addr, -EINVAL); | |
158 | ||
159 | addr->s_addr = lease->next_server; | |
160 | ||
161 | return 0; | |
162 | } | |
163 | ||
a6cc569e TG |
164 | sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { |
165 | if (lease) | |
166 | assert_se(REFCNT_INC(lease->n_ref) >= 2); | |
167 | ||
168 | return lease; | |
169 | } | |
170 | ||
171 | sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { | |
172 | if (lease && REFCNT_DEC(lease->n_ref) <= 0) { | |
173 | free(lease->hostname); | |
174 | free(lease->domainname); | |
175 | free(lease->dns); | |
176 | free(lease); | |
177 | } | |
178 | ||
179 | return NULL; | |
180 | } | |
181 | ||
182 | int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, | |
183 | void *user_data) { | |
184 | sd_dhcp_lease *lease = user_data; | |
185 | be32_t val; | |
186 | ||
187 | switch(code) { | |
188 | ||
189 | case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: | |
190 | if (len == 4) { | |
191 | memcpy(&val, option, 4); | |
192 | lease->lifetime = be32toh(val); | |
193 | } | |
194 | ||
195 | break; | |
196 | ||
197 | case DHCP_OPTION_SERVER_IDENTIFIER: | |
198 | if (len >= 4) | |
199 | memcpy(&lease->server_address, option, 4); | |
200 | ||
201 | break; | |
202 | ||
203 | case DHCP_OPTION_SUBNET_MASK: | |
204 | if (len >= 4) | |
205 | memcpy(&lease->subnet_mask, option, 4); | |
206 | ||
207 | break; | |
208 | ||
209 | case DHCP_OPTION_ROUTER: | |
210 | if (len >= 4) | |
211 | memcpy(&lease->router, option, 4); | |
212 | ||
213 | break; | |
214 | ||
215 | case DHCP_OPTION_DOMAIN_NAME_SERVER: | |
216 | if (len && !(len % 4)) { | |
a6cc569e TG |
217 | lease->dns_size = len / 4; |
218 | ||
219 | free(lease->dns); | |
0b21bde9 | 220 | lease->dns = newdup(struct in_addr, option, lease->dns_size); |
a6cc569e TG |
221 | if (!lease->dns) |
222 | return -ENOMEM; | |
46844696 TG |
223 | } |
224 | ||
225 | break; | |
226 | ||
227 | case DHCP_OPTION_NTP_SERVER: | |
228 | if (len && !(len % 4)) { | |
46844696 TG |
229 | lease->ntp_size = len / 4; |
230 | ||
231 | free(lease->ntp); | |
0b21bde9 | 232 | lease->ntp = newdup(struct in_addr, option, lease->ntp_size); |
46844696 TG |
233 | if (!lease->ntp) |
234 | return -ENOMEM; | |
a6cc569e TG |
235 | } |
236 | ||
237 | break; | |
238 | ||
239 | case DHCP_OPTION_INTERFACE_MTU: | |
240 | if (len >= 2) { | |
241 | be16_t mtu; | |
242 | ||
243 | memcpy(&mtu, option, 2); | |
244 | lease->mtu = be16toh(mtu); | |
245 | ||
246 | if (lease->mtu < 68) | |
247 | lease->mtu = 0; | |
248 | } | |
249 | ||
250 | break; | |
251 | ||
252 | case DHCP_OPTION_DOMAIN_NAME: | |
253 | if (len >= 1) { | |
254 | free(lease->domainname); | |
255 | lease->domainname = strndup((const char *)option, len); | |
256 | } | |
257 | ||
258 | break; | |
259 | ||
260 | case DHCP_OPTION_HOST_NAME: | |
261 | if (len >= 1) { | |
262 | free(lease->hostname); | |
263 | lease->hostname = strndup((const char *)option, len); | |
264 | } | |
265 | ||
266 | break; | |
267 | ||
ce78df79 TG |
268 | case DHCP_OPTION_ROOT_PATH: |
269 | if (len >= 1) { | |
270 | free(lease->root_path); | |
271 | lease->root_path = strndup((const char *)option, len); | |
272 | } | |
273 | ||
274 | break; | |
275 | ||
a6cc569e TG |
276 | case DHCP_OPTION_RENEWAL_T1_TIME: |
277 | if (len == 4) { | |
278 | memcpy(&val, option, 4); | |
279 | lease->t1 = be32toh(val); | |
280 | } | |
281 | ||
282 | break; | |
283 | ||
284 | case DHCP_OPTION_REBINDING_T2_TIME: | |
285 | if (len == 4) { | |
286 | memcpy(&val, option, 4); | |
287 | lease->t2 = be32toh(val); | |
288 | } | |
289 | ||
290 | break; | |
291 | } | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | int dhcp_lease_new(sd_dhcp_lease **ret) { | |
6e00a806 | 297 | sd_dhcp_lease *lease; |
a6cc569e TG |
298 | |
299 | lease = new0(sd_dhcp_lease, 1); | |
300 | if (!lease) | |
301 | return -ENOMEM; | |
302 | ||
8ddbeaa2 | 303 | lease->router = INADDR_ANY; |
a6cc569e TG |
304 | lease->n_ref = REFCNT_INIT; |
305 | ||
306 | *ret = lease; | |
a6cc569e TG |
307 | return 0; |
308 | } | |
fe8db0c5 | 309 | |
109731eb TG |
310 | static void serialize_addresses(FILE *f, const char *key, struct in_addr *addresses, size_t size) { |
311 | unsigned i; | |
312 | ||
313 | assert(key); | |
314 | assert(addresses); | |
315 | assert(size); | |
316 | ||
317 | fputs("DNS=", f); | |
318 | ||
319 | for (i = 0; i < size; i++) | |
320 | fprintf(f, "%s%s", inet_ntoa(addresses[i]), | |
321 | (i < (size - 1)) ? " ": ""); | |
322 | ||
323 | fputs("\n", f); | |
324 | } | |
325 | ||
326 | static int deserialize_addresses(struct in_addr **addresses, size_t *size, const char *string) { | |
327 | char *word, *state; | |
328 | size_t len; | |
329 | ||
330 | FOREACH_WORD(word, len, string, state) { | |
331 | struct in_addr *new_addresses; | |
332 | int r; | |
333 | ||
334 | new_addresses = realloc(*addresses, (*size + 1) * sizeof(struct in_addr)); | |
335 | if (!new_addresses) | |
336 | return -ENOMEM; | |
bc415566 TG |
337 | else |
338 | *addresses = new_addresses; | |
109731eb TG |
339 | |
340 | r = inet_aton(word, &(new_addresses[*size])); | |
341 | if (r < 0) | |
342 | continue; | |
343 | ||
109731eb TG |
344 | (*size)++; |
345 | } | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
fe8db0c5 TG |
350 | int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { |
351 | _cleanup_free_ char *temp_path = NULL; | |
352 | _cleanup_fclose_ FILE *f = NULL; | |
fe8db0c5 | 353 | struct in_addr address; |
109731eb TG |
354 | struct in_addr *addresses; |
355 | size_t addresses_size; | |
fe8db0c5 TG |
356 | const char *string; |
357 | uint16_t mtu; | |
358 | int r; | |
359 | ||
360 | assert(lease); | |
361 | assert(lease_file); | |
362 | ||
fe8db0c5 TG |
363 | r = fopen_temporary(lease_file, &f, &temp_path); |
364 | if (r < 0) | |
365 | goto finish; | |
366 | ||
367 | fchmod(fileno(f), 0644); | |
368 | ||
369 | r = sd_dhcp_lease_get_address(lease, &address); | |
370 | if (r < 0) | |
371 | goto finish; | |
372 | ||
fe8db0c5 TG |
373 | fprintf(f, |
374 | "# This is private data. Do not parse.\n" | |
109731eb | 375 | "ADDRESS=%s\n", inet_ntoa(address)); |
fe8db0c5 | 376 | |
fe8db0c5 TG |
377 | r = sd_dhcp_lease_get_netmask(lease, &address); |
378 | if (r < 0) | |
379 | goto finish; | |
380 | ||
109731eb | 381 | fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); |
fe8db0c5 | 382 | |
8ddbeaa2 UTL |
383 | r = sd_dhcp_lease_get_router(lease, &address); |
384 | if (r >= 0) | |
385 | fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); | |
386 | ||
0ad853bc | 387 | r = sd_dhcp_lease_get_server_identifier(lease, &address); |
109731eb TG |
388 | if (r >= 0) |
389 | fprintf(f, "SERVER_ADDRESS=%s\n", | |
390 | inet_ntoa(address)); | |
0ad853bc | 391 | |
8e34a618 | 392 | r = sd_dhcp_lease_get_next_server(lease, &address); |
109731eb TG |
393 | if (r >= 0) |
394 | fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); | |
8e34a618 | 395 | |
fe8db0c5 TG |
396 | r = sd_dhcp_lease_get_mtu(lease, &mtu); |
397 | if (r >= 0) | |
398 | fprintf(f, "MTU=%" PRIu16 "\n", mtu); | |
399 | ||
109731eb TG |
400 | r = sd_dhcp_lease_get_dns(lease, &addresses, &addresses_size); |
401 | if (r >= 0) | |
402 | serialize_addresses(f, "DNS", addresses, addresses_size); | |
403 | ||
404 | r = sd_dhcp_lease_get_ntp(lease, &addresses, &addresses_size); | |
405 | if (r >= 0) | |
406 | serialize_addresses(f, "NTP", addresses, addresses_size); | |
fe8db0c5 TG |
407 | |
408 | r = sd_dhcp_lease_get_domainname(lease, &string); | |
409 | if (r >= 0) | |
410 | fprintf(f, "DOMAINNAME=%s\n", string); | |
411 | ||
412 | r = sd_dhcp_lease_get_hostname(lease, &string); | |
413 | if (r >= 0) | |
414 | fprintf(f, "HOSTNAME=%s\n", string); | |
415 | ||
ce78df79 TG |
416 | r = sd_dhcp_lease_get_root_path(lease, &string); |
417 | if (r >= 0) | |
418 | fprintf(f, "ROOT_PATH=%s\n", string); | |
419 | ||
fe8db0c5 TG |
420 | r = 0; |
421 | ||
422 | fflush(f); | |
423 | ||
424 | if (ferror(f) || rename(temp_path, lease_file) < 0) { | |
425 | r = -errno; | |
426 | unlink(lease_file); | |
427 | unlink(temp_path); | |
428 | } | |
429 | ||
430 | finish: | |
431 | if (r < 0) | |
432 | log_error("Failed to save lease data %s: %s", lease_file, strerror(-r)); | |
433 | ||
434 | return r; | |
435 | } | |
436 | ||
437 | int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) { | |
438 | _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; | |
439 | _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL, | |
8e34a618 | 440 | *server_address = NULL, *next_server = NULL, |
109731eb | 441 | *dns = NULL, *ntp = NULL, *mtu = NULL; |
fe8db0c5 TG |
442 | struct in_addr addr; |
443 | int r; | |
444 | ||
445 | assert(lease_file); | |
446 | assert(ret); | |
447 | ||
448 | r = dhcp_lease_new(&lease); | |
449 | if (r < 0) | |
450 | return r; | |
451 | ||
452 | r = parse_env_file(lease_file, NEWLINE, | |
453 | "ADDRESS", &address, | |
454 | "ROUTER", &router, | |
455 | "NETMASK", &netmask, | |
0ad853bc | 456 | "SERVER_IDENTIFIER", &server_address, |
8e34a618 | 457 | "NEXT_SERVER", &next_server, |
109731eb TG |
458 | "DNS", &dns, |
459 | "NTP", &ntp, | |
fe8db0c5 TG |
460 | "MTU", &mtu, |
461 | "DOMAINNAME", &lease->domainname, | |
462 | "HOSTNAME", &lease->hostname, | |
ce78df79 | 463 | "ROOT_PATH", &lease->root_path, |
fe8db0c5 TG |
464 | NULL); |
465 | if (r < 0) { | |
466 | if (r == -ENOENT) | |
467 | return 0; | |
468 | ||
469 | log_error("Failed to read %s: %s", lease_file, strerror(-r)); | |
470 | return r; | |
471 | } | |
472 | ||
473 | r = inet_pton(AF_INET, address, &addr); | |
474 | if (r < 0) | |
475 | return r; | |
476 | ||
477 | lease->address = addr.s_addr; | |
478 | ||
8ddbeaa2 UTL |
479 | if (router) { |
480 | r = inet_pton(AF_INET, router, &addr); | |
481 | if (r < 0) | |
482 | return r; | |
fe8db0c5 | 483 | |
8ddbeaa2 UTL |
484 | lease->router = addr.s_addr; |
485 | } | |
fe8db0c5 TG |
486 | |
487 | r = inet_pton(AF_INET, netmask, &addr); | |
488 | if (r < 0) | |
489 | return r; | |
490 | ||
491 | lease->subnet_mask = addr.s_addr; | |
492 | ||
0ad853bc TG |
493 | if (server_address) { |
494 | r = inet_pton(AF_INET, server_address, &addr); | |
495 | if (r < 0) | |
496 | return r; | |
497 | ||
498 | lease->server_address = addr.s_addr; | |
499 | } | |
500 | ||
8e34a618 TG |
501 | if (next_server) { |
502 | r = inet_pton(AF_INET, next_server, &addr); | |
503 | if (r < 0) | |
504 | return r; | |
505 | ||
506 | lease->next_server = addr.s_addr; | |
507 | } | |
508 | ||
109731eb TG |
509 | if (dns) { |
510 | r = deserialize_addresses(&lease->dns, &lease->dns_size, dns); | |
511 | if (r < 0) | |
512 | return r; | |
513 | } | |
514 | ||
515 | if (ntp) { | |
516 | r = deserialize_addresses(&lease->ntp, &lease->ntp_size, dns); | |
517 | if (r < 0) | |
518 | return r; | |
519 | } | |
520 | ||
fe8db0c5 TG |
521 | if (mtu) { |
522 | uint16_t u; | |
523 | if (sscanf(mtu, "%" SCNu16, &u) > 0) | |
524 | lease->mtu = u; | |
525 | } | |
526 | ||
527 | *ret = lease; | |
528 | lease = NULL; | |
529 | ||
530 | return 0; | |
531 | } | |
9e64dd72 TG |
532 | |
533 | int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { | |
534 | uint32_t address; | |
535 | ||
536 | assert(lease); | |
537 | assert(lease->address != INADDR_ANY); | |
538 | ||
539 | address = be32toh(lease->address); | |
540 | ||
541 | /* fall back to the default subnet masks based on address class */ | |
542 | ||
543 | if ((address >> 31) == 0x0) | |
544 | /* class A, leading bits: 0 */ | |
545 | lease->subnet_mask = htobe32(0xff000000); | |
546 | else if ((address >> 30) == 0x2) | |
547 | /* class B, leading bits 10 */ | |
548 | lease->subnet_mask = htobe32(0xffff0000); | |
549 | else if ((address >> 29) == 0x6) | |
550 | /* class C, leading bits 110 */ | |
551 | lease->subnet_mask = htobe32(0xffffff00); | |
552 | else | |
553 | /* class D or E, no default mask. give up */ | |
554 | return -ERANGE; | |
555 | ||
556 | return 0; | |
557 | } |