chiark / gitweb /
update TODO
[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
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
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
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
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
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
129         if (lease->router != INADDR_ANY)
130                 addr->s_addr = lease->router;
131         else
132                 return -ENOENT;
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
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
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
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)) {
217                         lease->dns_size = len / 4;
218
219                         free(lease->dns);
220                         lease->dns = newdup(struct in_addr, option, lease->dns_size);
221                         if (!lease->dns)
222                                 return -ENOMEM;
223                 }
224
225                 break;
226
227         case DHCP_OPTION_NTP_SERVER:
228                 if (len && !(len % 4)) {
229                         lease->ntp_size = len / 4;
230
231                         free(lease->ntp);
232                         lease->ntp = newdup(struct in_addr, option, lease->ntp_size);
233                         if (!lease->ntp)
234                                 return -ENOMEM;
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
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
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) {
297         sd_dhcp_lease *lease;
298
299         lease = new0(sd_dhcp_lease, 1);
300         if (!lease)
301                 return -ENOMEM;
302
303         lease->router = INADDR_ANY;
304         lease->n_ref = REFCNT_INIT;
305
306         *ret = lease;
307         return 0;
308 }
309
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;
337                 else
338                         *addresses = new_addresses;
339
340                 r = inet_aton(word, &(new_addresses[*size]));
341                 if (r < 0)
342                         continue;
343
344                 (*size)++;
345         }
346
347         return 0;
348 }
349
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;
353         struct in_addr address;
354         struct in_addr *addresses;
355         size_t addresses_size;
356         const char *string;
357         uint16_t mtu;
358         int r;
359
360         assert(lease);
361         assert(lease_file);
362
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
373         fprintf(f,
374                 "# This is private data. Do not parse.\n"
375                 "ADDRESS=%s\n", inet_ntoa(address));
376
377         r = sd_dhcp_lease_get_netmask(lease, &address);
378         if (r < 0)
379                 goto finish;
380
381         fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
382
383         r = sd_dhcp_lease_get_router(lease, &address);
384         if (r >= 0)
385                 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
386
387         r = sd_dhcp_lease_get_server_identifier(lease, &address);
388         if (r >= 0)
389                 fprintf(f, "SERVER_ADDRESS=%s\n",
390                         inet_ntoa(address));
391
392         r = sd_dhcp_lease_get_next_server(lease, &address);
393         if (r >= 0)
394                 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
395
396         r = sd_dhcp_lease_get_mtu(lease, &mtu);
397         if (r >= 0)
398                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
399
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);
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
416         r = sd_dhcp_lease_get_root_path(lease, &string);
417         if (r >= 0)
418                 fprintf(f, "ROOT_PATH=%s\n", string);
419
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,
440                             *server_address = NULL, *next_server = NULL,
441                             *dns = NULL, *ntp = NULL, *mtu = NULL;
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,
456                            "SERVER_IDENTIFIER", &server_address,
457                            "NEXT_SERVER", &next_server,
458                            "DNS", &dns,
459                            "NTP", &ntp,
460                            "MTU", &mtu,
461                            "DOMAINNAME", &lease->domainname,
462                            "HOSTNAME", &lease->hostname,
463                            "ROOT_PATH", &lease->root_path,
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
479         if (router) {
480                 r = inet_pton(AF_INET, router, &addr);
481                 if (r < 0)
482                         return r;
483
484                 lease->router = addr.s_addr;
485         }
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
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
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
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
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 }
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 }