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