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