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