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