chiark / gitweb /
sd-dhcp6-lease: Add helper function to compute remaining expiry time
[elogind.git] / src / libsystemd-network / sd-dhcp-lease.c
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>
26 #include <arpa/inet.h>
27 #include <sys/param.h>
28
29 #include "util.h"
30 #include "list.h"
31 #include "mkdir.h"
32 #include "fileio.h"
33
34 #include "dhcp-protocol.h"
35 #include "dhcp-internal.h"
36 #include "dhcp-lease-internal.h"
37 #include "sd-dhcp-lease.h"
38 #include "sd-dhcp-client.h"
39 #include "network-internal.h"
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
50 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
51         assert_return(lease, -EINVAL);
52         assert_return(mtu, -EINVAL);
53
54         if (lease->mtu)
55                 *mtu = lease->mtu;
56         else
57                 return -ENOENT;
58
59         return 0;
60 }
61
62 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
63         assert_return(lease, -EINVAL);
64         assert_return(addr, -EINVAL);
65         assert_return(addr_size, -EINVAL);
66
67         if (lease->dns_size) {
68                 *addr_size = lease->dns_size;
69                 *addr = lease->dns;
70         } else
71                 return -ENOENT;
72
73         return 0;
74 }
75
76 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, struct in_addr **addr, size_t *addr_size) {
77         assert_return(lease, -EINVAL);
78         assert_return(addr, -EINVAL);
79         assert_return(addr_size, -EINVAL);
80
81         if (lease->ntp_size) {
82                 *addr_size = lease->ntp_size;
83                 *addr = lease->ntp;
84         } else
85                 return -ENOENT;
86
87         return 0;
88 }
89
90 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
91         assert_return(lease, -EINVAL);
92         assert_return(domainname, -EINVAL);
93
94         if (lease->domainname)
95                 *domainname = lease->domainname;
96         else
97                 return -ENOENT;
98
99         return 0;
100 }
101
102 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
103         assert_return(lease, -EINVAL);
104         assert_return(hostname, -EINVAL);
105
106         if (lease->hostname)
107                 *hostname = lease->hostname;
108         else
109                 return -ENOENT;
110
111         return 0;
112 }
113
114 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
115         assert_return(lease, -EINVAL);
116         assert_return(root_path, -EINVAL);
117
118         if (lease->root_path)
119                 *root_path = lease->root_path;
120         else
121                 return -ENOENT;
122
123         return 0;
124 }
125
126 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
127         assert_return(lease, -EINVAL);
128         assert_return(addr, -EINVAL);
129
130         if (lease->router != INADDR_ANY)
131                 addr->s_addr = lease->router;
132         else
133                 return -ENOENT;
134
135         return 0;
136 }
137
138 int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
139         assert_return(lease, -EINVAL);
140         assert_return(addr, -EINVAL);
141
142         addr->s_addr = lease->subnet_mask;
143
144         return 0;
145 }
146
147 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
148         assert_return(lease, -EINVAL);
149         assert_return(addr, -EINVAL);
150
151         addr->s_addr = lease->server_address;
152
153         return 0;
154 }
155
156 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
157         assert_return(lease, -EINVAL);
158         assert_return(addr, -EINVAL);
159
160         addr->s_addr = lease->next_server;
161
162         return 0;
163 }
164
165 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
166         if (lease)
167                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
168
169         return lease;
170 }
171
172 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
173         if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
174                 free(lease->hostname);
175                 free(lease->domainname);
176                 free(lease->dns);
177                 free(lease->ntp);
178                 free(lease);
179         }
180
181         return NULL;
182 }
183
184 static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
185         be32_t val;
186
187         assert(option);
188         assert(ret);
189
190         if (len == 4) {
191                 memcpy(&val, option, 4);
192                 *ret = be32toh(val);
193
194                 if (*ret < min)
195                         *ret = min;
196         }
197 }
198
199 static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
200         lease_parse_u32(option, len, (uint32_t *)ret, 0);
201 }
202
203 static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
204         be16_t val;
205
206         assert(option);
207         assert(ret);
208
209         if (len == 2) {
210                 memcpy(&val, option, 2);
211                 *ret = be16toh(val);
212
213                 if (*ret < min)
214                         *ret = min;
215         }
216 }
217
218 static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
219         assert(option);
220         assert(ret);
221
222         if (len == 4)
223                 memcpy(ret, option, 4);
224 }
225
226 static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
227         assert(option);
228         assert(ret);
229
230         if (len == 1)
231                 *ret = !!(*option);
232 }
233
234 static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
235         assert(option);
236         assert(ret);
237
238         if (len == 1) {
239                 *ret = *option;
240
241                 if (*ret < min)
242                         *ret = min;
243         }
244 }
245
246 static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
247         assert(option);
248         assert(ret);
249
250         if (len >= 1) {
251                 char *string;
252
253                 string = strndup((const char *)option, len);
254                 if (!string)
255                         return -errno;
256
257                 free(*ret);
258                 *ret = string;
259         }
260
261         return 0;
262 }
263
264 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) {
265         assert(option);
266         assert(ret);
267         assert(ret_size);
268
269         if (len && !(len % (4 * mult))) {
270                 size_t size;
271                 struct in_addr *addresses;
272
273                 size = len / 4;
274
275                 addresses = newdup(struct in_addr, option, size);
276                 if (!addresses)
277                         return -ENOMEM;
278
279                 free(*ret);
280                 *ret = addresses;
281                 *ret_size = size;
282         }
283
284         return 0;
285 }
286
287 static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
288         return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
289 }
290
291 static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
292         return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
293 }
294
295 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
296                               void *user_data) {
297         sd_dhcp_lease *lease = user_data;
298         int r;
299
300         assert(lease);
301
302         switch(code) {
303
304         case DHCP_OPTION_TIME_OFFSET:
305                 lease_parse_s32(option, len, &lease->time_offset);
306
307                 break;
308
309         case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
310                 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
311
312                 break;
313
314         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
315                 lease_parse_u32(option, len, &lease->lifetime, 1);
316
317                 break;
318
319         case DHCP_OPTION_SERVER_IDENTIFIER:
320                 lease_parse_be32(option, len, &lease->server_address);
321
322                 break;
323
324         case DHCP_OPTION_SUBNET_MASK:
325                 lease_parse_be32(option, len, &lease->subnet_mask);
326
327                 break;
328
329         case DHCP_OPTION_BROADCAST:
330                 lease_parse_be32(option, len, &lease->broadcast);
331
332                 break;
333
334         case DHCP_OPTION_ROUTER:
335                 lease_parse_be32(option, len, &lease->router);
336
337                 break;
338
339         case DHCP_OPTION_DOMAIN_NAME_SERVER:
340                 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
341                 if (r < 0)
342                         return r;
343
344                 break;
345
346         case DHCP_OPTION_NTP_SERVER:
347                 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
348                 if (r < 0)
349                         return r;
350
351                 break;
352
353         case DHCP_OPTION_POLICY_FILTER:
354                 r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
355                 if (r < 0)
356                         return r;
357
358                 break;
359
360         case DHCP_OPTION_STATIC_ROUTE:
361                 r = lease_parse_in_addrs_pairs(option, len, &lease->static_route, &lease->static_route_size);
362                 if (r < 0)
363                         return r;
364
365                 break;
366
367         case DHCP_OPTION_INTERFACE_MTU:
368                 lease_parse_u16(option, len, &lease->mtu, 68);
369
370                 break;
371
372         case DHCP_OPTION_INTERFACE_MDR:
373                 lease_parse_u16(option, len, &lease->mdr, 576);
374
375                 break;
376
377         case DHCP_OPTION_INTERFACE_TTL:
378                 lease_parse_u8(option, len, &lease->ttl, 1);
379
380                 break;
381
382         case DHCP_OPTION_BOOT_FILE_SIZE:
383                 lease_parse_u16(option, len, &lease->boot_file_size, 0);
384
385                 break;
386
387         case DHCP_OPTION_DOMAIN_NAME:
388                 r = lease_parse_string(option, len, &lease->domainname);
389                 if (r < 0)
390                         return r;
391
392                 break;
393
394         case DHCP_OPTION_HOST_NAME:
395                 r = lease_parse_string(option, len, &lease->hostname);
396                 if (r < 0)
397                         return r;
398
399                 break;
400
401         case DHCP_OPTION_ROOT_PATH:
402                 r = lease_parse_string(option, len, &lease->root_path);
403                 if (r < 0)
404                         return r;
405
406                 break;
407
408         case DHCP_OPTION_RENEWAL_T1_TIME:
409                 lease_parse_u32(option, len, &lease->t1, 1);
410
411                 break;
412
413         case DHCP_OPTION_REBINDING_T2_TIME:
414                 lease_parse_u32(option, len, &lease->t2, 1);
415
416                 break;
417
418         case DHCP_OPTION_ENABLE_IP_FORWARDING:
419                 lease_parse_bool(option, len, &lease->ip_forward);
420
421                 break;
422
423         case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
424                 lease_parse_bool(option, len, &lease->ip_forward_non_local);
425
426                 break;
427         }
428
429         return 0;
430 }
431
432 int dhcp_lease_new(sd_dhcp_lease **ret) {
433         sd_dhcp_lease *lease;
434
435         lease = new0(sd_dhcp_lease, 1);
436         if (!lease)
437                 return -ENOMEM;
438
439         lease->router = INADDR_ANY;
440         lease->n_ref = REFCNT_INIT;
441
442         *ret = lease;
443         return 0;
444 }
445
446 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
447         _cleanup_free_ char *temp_path = NULL;
448         _cleanup_fclose_ FILE *f = NULL;
449         struct in_addr address;
450         struct in_addr *addresses;
451         size_t addresses_size;
452         const char *string;
453         uint16_t mtu;
454         int r;
455
456         assert(lease);
457         assert(lease_file);
458
459         r = fopen_temporary(lease_file, &f, &temp_path);
460         if (r < 0)
461                 goto finish;
462
463         fchmod(fileno(f), 0644);
464
465         r = sd_dhcp_lease_get_address(lease, &address);
466         if (r < 0)
467                 goto finish;
468
469         fprintf(f,
470                 "# This is private data. Do not parse.\n"
471                 "ADDRESS=%s\n", inet_ntoa(address));
472
473         r = sd_dhcp_lease_get_netmask(lease, &address);
474         if (r < 0)
475                 goto finish;
476
477         fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
478
479         r = sd_dhcp_lease_get_router(lease, &address);
480         if (r >= 0)
481                 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
482
483         r = sd_dhcp_lease_get_server_identifier(lease, &address);
484         if (r >= 0)
485                 fprintf(f, "SERVER_ADDRESS=%s\n",
486                         inet_ntoa(address));
487
488         r = sd_dhcp_lease_get_next_server(lease, &address);
489         if (r >= 0)
490                 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
491
492         r = sd_dhcp_lease_get_mtu(lease, &mtu);
493         if (r >= 0)
494                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
495
496         r = sd_dhcp_lease_get_dns(lease, &addresses, &addresses_size);
497         if (r >= 0)
498                 serialize_in_addrs(f, "DNS", addresses, addresses_size);
499
500         r = sd_dhcp_lease_get_ntp(lease, &addresses, &addresses_size);
501         if (r >= 0)
502                 serialize_in_addrs(f, "NTP", addresses, addresses_size);
503
504         r = sd_dhcp_lease_get_domainname(lease, &string);
505         if (r >= 0)
506                 fprintf(f, "DOMAINNAME=%s\n", string);
507
508         r = sd_dhcp_lease_get_hostname(lease, &string);
509         if (r >= 0)
510                 fprintf(f, "HOSTNAME=%s\n", string);
511
512         r = sd_dhcp_lease_get_root_path(lease, &string);
513         if (r >= 0)
514                 fprintf(f, "ROOT_PATH=%s\n", string);
515
516         r = 0;
517
518         fflush(f);
519
520         if (ferror(f) || rename(temp_path, lease_file) < 0) {
521                 r = -errno;
522                 unlink(lease_file);
523                 unlink(temp_path);
524         }
525
526 finish:
527         if (r < 0)
528                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
529
530         return r;
531 }
532
533 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
534         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
535         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
536                             *server_address = NULL, *next_server = NULL,
537                             *dns = NULL, *ntp = NULL, *mtu = NULL;
538         struct in_addr addr;
539         int r;
540
541         assert(lease_file);
542         assert(ret);
543
544         r = dhcp_lease_new(&lease);
545         if (r < 0)
546                 return r;
547
548         r = parse_env_file(lease_file, NEWLINE,
549                            "ADDRESS", &address,
550                            "ROUTER", &router,
551                            "NETMASK", &netmask,
552                            "SERVER_IDENTIFIER", &server_address,
553                            "NEXT_SERVER", &next_server,
554                            "DNS", &dns,
555                            "NTP", &ntp,
556                            "MTU", &mtu,
557                            "DOMAINNAME", &lease->domainname,
558                            "HOSTNAME", &lease->hostname,
559                            "ROOT_PATH", &lease->root_path,
560                            NULL);
561         if (r < 0) {
562                 if (r == -ENOENT)
563                         return 0;
564
565                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
566                 return r;
567         }
568
569         r = inet_pton(AF_INET, address, &addr);
570         if (r < 0)
571                 return r;
572
573         lease->address = addr.s_addr;
574
575         if (router) {
576                 r = inet_pton(AF_INET, router, &addr);
577                 if (r < 0)
578                         return r;
579
580                 lease->router = addr.s_addr;
581         }
582
583         r = inet_pton(AF_INET, netmask, &addr);
584         if (r < 0)
585                 return r;
586
587         lease->subnet_mask = addr.s_addr;
588
589         if (server_address) {
590                 r = inet_pton(AF_INET, server_address, &addr);
591                 if (r < 0)
592                         return r;
593
594                 lease->server_address = addr.s_addr;
595         }
596
597         if (next_server) {
598                 r = inet_pton(AF_INET, next_server, &addr);
599                 if (r < 0)
600                         return r;
601
602                 lease->next_server = addr.s_addr;
603         }
604
605         if (dns) {
606                 r = deserialize_in_addrs(&lease->dns, &lease->dns_size, dns);
607                 if (r < 0)
608                         return r;
609         }
610
611         if (ntp) {
612                 r = deserialize_in_addrs(&lease->ntp, &lease->ntp_size, dns);
613                 if (r < 0)
614                         return r;
615         }
616
617         if (mtu) {
618                 uint16_t u;
619                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
620                         lease->mtu = u;
621         }
622
623         *ret = lease;
624         lease = NULL;
625
626         return 0;
627 }
628
629 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
630         uint32_t address;
631
632         assert(lease);
633         assert(lease->address != INADDR_ANY);
634
635         address = be32toh(lease->address);
636
637         /* fall back to the default subnet masks based on address class */
638
639         if ((address >> 31) == 0x0)
640                 /* class A, leading bits: 0 */
641                 lease->subnet_mask = htobe32(0xff000000);
642         else if ((address >> 30) == 0x2)
643                 /* class B, leading bits 10 */
644                 lease->subnet_mask = htobe32(0xffff0000);
645         else if ((address >> 29) == 0x6)
646                 /* class C, leading bits 110 */
647                 lease->subnet_mask = htobe32(0xffffff00);
648         else
649                 /* class D or E, no default mask. give up */
650                 return -ERANGE;
651
652         return 0;
653 }