chiark / gitweb /
acdcca87ecf61bc5c4a691e8924347de04ad9e3c
[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                 else
334                         *addresses = new_addresses;
335
336                 r = inet_aton(word, &(new_addresses[*size]));
337                 if (r < 0)
338                         continue;
339
340                 (*size)++;
341         }
342
343         return 0;
344 }
345
346 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
347         _cleanup_free_ char *temp_path = NULL;
348         _cleanup_fclose_ FILE *f = NULL;
349         struct in_addr address;
350         struct in_addr *addresses;
351         size_t addresses_size;
352         const char *string;
353         uint16_t mtu;
354         int r;
355
356         assert(lease);
357         assert(lease_file);
358
359         r = fopen_temporary(lease_file, &f, &temp_path);
360         if (r < 0)
361                 goto finish;
362
363         fchmod(fileno(f), 0644);
364
365         r = sd_dhcp_lease_get_address(lease, &address);
366         if (r < 0)
367                 goto finish;
368
369         fprintf(f,
370                 "# This is private data. Do not parse.\n"
371                 "ADDRESS=%s\n", inet_ntoa(address));
372
373         r = sd_dhcp_lease_get_router(lease, &address);
374         if (r < 0)
375                 goto finish;
376
377         fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
378
379         r = sd_dhcp_lease_get_netmask(lease, &address);
380         if (r < 0)
381                 goto finish;
382
383         fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
384
385         r = sd_dhcp_lease_get_server_identifier(lease, &address);
386         if (r >= 0)
387                 fprintf(f, "SERVER_ADDRESS=%s\n",
388                         inet_ntoa(address));
389
390         r = sd_dhcp_lease_get_next_server(lease, &address);
391         if (r >= 0)
392                 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
393
394         r = sd_dhcp_lease_get_mtu(lease, &mtu);
395         if (r >= 0)
396                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
397
398         r = sd_dhcp_lease_get_dns(lease, &addresses, &addresses_size);
399         if (r >= 0)
400                 serialize_addresses(f, "DNS", addresses, addresses_size);
401
402         r = sd_dhcp_lease_get_ntp(lease, &addresses, &addresses_size);
403         if (r >= 0)
404                 serialize_addresses(f, "NTP", addresses, addresses_size);
405
406         r = sd_dhcp_lease_get_domainname(lease, &string);
407         if (r >= 0)
408                 fprintf(f, "DOMAINNAME=%s\n", string);
409
410         r = sd_dhcp_lease_get_hostname(lease, &string);
411         if (r >= 0)
412                 fprintf(f, "HOSTNAME=%s\n", string);
413
414         r = sd_dhcp_lease_get_root_path(lease, &string);
415         if (r >= 0)
416                 fprintf(f, "ROOT_PATH=%s\n", string);
417
418         r = 0;
419
420         fflush(f);
421
422         if (ferror(f) || rename(temp_path, lease_file) < 0) {
423                 r = -errno;
424                 unlink(lease_file);
425                 unlink(temp_path);
426         }
427
428 finish:
429         if (r < 0)
430                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
431
432         return r;
433 }
434
435 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
436         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
437         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
438                             *server_address = NULL, *next_server = NULL,
439                             *dns = NULL, *ntp = NULL, *mtu = NULL;
440         struct in_addr addr;
441         int r;
442
443         assert(lease_file);
444         assert(ret);
445
446         r = dhcp_lease_new(&lease);
447         if (r < 0)
448                 return r;
449
450         r = parse_env_file(lease_file, NEWLINE,
451                            "ADDRESS", &address,
452                            "ROUTER", &router,
453                            "NETMASK", &netmask,
454                            "SERVER_IDENTIFIER", &server_address,
455                            "NEXT_SERVER", &next_server,
456                            "DNS", &dns,
457                            "NTP", &ntp,
458                            "MTU", &mtu,
459                            "DOMAINNAME", &lease->domainname,
460                            "HOSTNAME", &lease->hostname,
461                            "ROOT_PATH", &lease->root_path,
462                            NULL);
463         if (r < 0) {
464                 if (r == -ENOENT)
465                         return 0;
466
467                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
468                 return r;
469         }
470
471         r = inet_pton(AF_INET, address, &addr);
472         if (r < 0)
473                 return r;
474
475         lease->address = addr.s_addr;
476
477         r = inet_pton(AF_INET, router, &addr);
478         if (r < 0)
479                 return r;
480
481         lease->router = addr.s_addr;
482
483         r = inet_pton(AF_INET, netmask, &addr);
484         if (r < 0)
485                 return r;
486
487         lease->subnet_mask = addr.s_addr;
488
489         if (server_address) {
490                 r = inet_pton(AF_INET, server_address, &addr);
491                 if (r < 0)
492                         return r;
493
494                 lease->server_address = addr.s_addr;
495         }
496
497         if (next_server) {
498                 r = inet_pton(AF_INET, next_server, &addr);
499                 if (r < 0)
500                         return r;
501
502                 lease->next_server = addr.s_addr;
503         }
504
505         if (dns) {
506                 r = deserialize_addresses(&lease->dns, &lease->dns_size, dns);
507                 if (r < 0)
508                         return r;
509         }
510
511         if (ntp) {
512                 r = deserialize_addresses(&lease->ntp, &lease->ntp_size, dns);
513                 if (r < 0)
514                         return r;
515         }
516
517         if (mtu) {
518                 uint16_t u;
519                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
520                         lease->mtu = u;
521         }
522
523         *ret = lease;
524         lease = NULL;
525
526         return 0;
527 }
528
529 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
530         uint32_t address;
531
532         assert(lease);
533         assert(lease->address != INADDR_ANY);
534
535         address = be32toh(lease->address);
536
537         /* fall back to the default subnet masks based on address class */
538
539         if ((address >> 31) == 0x0)
540                 /* class A, leading bits: 0 */
541                 lease->subnet_mask = htobe32(0xff000000);
542         else if ((address >> 30) == 0x2)
543                 /* class B, leading bits 10 */
544                 lease->subnet_mask = htobe32(0xffff0000);
545         else if ((address >> 29) == 0x6)
546                 /* class C, leading bits 110 */
547                 lease->subnet_mask = htobe32(0xffffff00);
548         else
549                 /* class D or E, no default mask. give up */
550                 return -ERANGE;
551
552         return 0;
553 }