chiark / gitweb /
test-dhcp-client: unref lease objects to make valgrind happy
[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_domainname(sd_dhcp_lease *lease, const char **domainname) {
76         assert_return(lease, -EINVAL);
77         assert_return(domainname, -EINVAL);
78
79         if (lease->domainname)
80                 *domainname = lease->domainname;
81         else
82                 return -ENOENT;
83
84         return 0;
85 }
86
87 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
88         assert_return(lease, -EINVAL);
89         assert_return(hostname, -EINVAL);
90
91         if (lease->hostname)
92                 *hostname = lease->hostname;
93         else
94                 return -ENOENT;
95
96         return 0;
97 }
98
99 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
100         assert_return(lease, -EINVAL);
101         assert_return(root_path, -EINVAL);
102
103         if (lease->root_path)
104                 *root_path = lease->root_path;
105         else
106                 return -ENOENT;
107
108         return 0;
109 }
110
111 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
112         assert_return(lease, -EINVAL);
113         assert_return(addr, -EINVAL);
114
115         addr->s_addr = lease->router;
116
117         return 0;
118 }
119
120 int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
121         assert_return(lease, -EINVAL);
122         assert_return(addr, -EINVAL);
123
124         addr->s_addr = lease->subnet_mask;
125
126         return 0;
127 }
128
129 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
130         assert_return(lease, -EINVAL);
131         assert_return(addr, -EINVAL);
132
133         addr->s_addr = lease->server_address;
134
135         return 0;
136 }
137
138 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
139         assert_return(lease, -EINVAL);
140         assert_return(addr, -EINVAL);
141
142         addr->s_addr = lease->next_server;
143
144         return 0;
145 }
146
147 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
148         if (lease)
149                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
150
151         return lease;
152 }
153
154 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
155         if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
156                 free(lease->hostname);
157                 free(lease->domainname);
158                 free(lease->dns);
159                 free(lease);
160         }
161
162         return NULL;
163 }
164
165 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
166                               void *user_data) {
167         sd_dhcp_lease *lease = user_data;
168         be32_t val;
169
170         switch(code) {
171
172         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
173                 if (len == 4) {
174                         memcpy(&val, option, 4);
175                         lease->lifetime = be32toh(val);
176                 }
177
178                 break;
179
180         case DHCP_OPTION_SERVER_IDENTIFIER:
181                 if (len >= 4)
182                         memcpy(&lease->server_address, option, 4);
183
184                 break;
185
186         case DHCP_OPTION_SUBNET_MASK:
187                 if (len >= 4)
188                         memcpy(&lease->subnet_mask, option, 4);
189
190                 break;
191
192         case DHCP_OPTION_ROUTER:
193                 if (len >= 4)
194                         memcpy(&lease->router, option, 4);
195
196                 break;
197
198         case DHCP_OPTION_DOMAIN_NAME_SERVER:
199                 if (len && !(len % 4)) {
200                         unsigned i;
201
202                         lease->dns_size = len / 4;
203
204                         free(lease->dns);
205                         lease->dns = new0(struct in_addr, lease->dns_size);
206                         if (!lease->dns)
207                                 return -ENOMEM;
208
209                         for (i = 0; i < lease->dns_size; i++) {
210                                 memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
211                         }
212                 }
213
214                 break;
215
216         case DHCP_OPTION_INTERFACE_MTU:
217                 if (len >= 2) {
218                         be16_t mtu;
219
220                         memcpy(&mtu, option, 2);
221                         lease->mtu = be16toh(mtu);
222
223                         if (lease->mtu < 68)
224                                 lease->mtu = 0;
225                 }
226
227                 break;
228
229         case DHCP_OPTION_DOMAIN_NAME:
230                 if (len >= 1) {
231                         free(lease->domainname);
232                         lease->domainname = strndup((const char *)option, len);
233                 }
234
235                 break;
236
237         case DHCP_OPTION_HOST_NAME:
238                 if (len >= 1) {
239                         free(lease->hostname);
240                         lease->hostname = strndup((const char *)option, len);
241                 }
242
243                 break;
244
245         case DHCP_OPTION_ROOT_PATH:
246                 if (len >= 1) {
247                         free(lease->root_path);
248                         lease->root_path = strndup((const char *)option, len);
249                 }
250
251                 break;
252
253         case DHCP_OPTION_RENEWAL_T1_TIME:
254                 if (len == 4) {
255                         memcpy(&val, option, 4);
256                         lease->t1 = be32toh(val);
257                 }
258
259                 break;
260
261         case DHCP_OPTION_REBINDING_T2_TIME:
262                 if (len == 4) {
263                         memcpy(&val, option, 4);
264                         lease->t2 = be32toh(val);
265                 }
266
267                 break;
268         }
269
270         return 0;
271 }
272
273 int dhcp_lease_new(sd_dhcp_lease **ret) {
274         sd_dhcp_lease *lease;
275
276         lease = new0(sd_dhcp_lease, 1);
277         if (!lease)
278                 return -ENOMEM;
279
280         lease->n_ref = REFCNT_INIT;
281
282         *ret = lease;
283         return 0;
284 }
285
286 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
287         _cleanup_free_ char *temp_path = NULL;
288         _cleanup_fclose_ FILE *f = NULL;
289         char buf[INET_ADDRSTRLEN];
290         struct in_addr address;
291         const char *string;
292         uint16_t mtu;
293         int r;
294
295         assert(lease);
296         assert(lease_file);
297
298         r = fopen_temporary(lease_file, &f, &temp_path);
299         if (r < 0)
300                 goto finish;
301
302         fchmod(fileno(f), 0644);
303
304         r = sd_dhcp_lease_get_address(lease, &address);
305         if (r < 0)
306                 goto finish;
307
308         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
309         if (!string) {
310                 r = -errno;
311                 goto finish;
312         }
313
314         fprintf(f,
315                 "# This is private data. Do not parse.\n"
316                 "ADDRESS=%s\n", string);
317
318         r = sd_dhcp_lease_get_router(lease, &address);
319         if (r < 0)
320                 goto finish;
321
322         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
323         if (!string) {
324                 r = -errno;
325                 goto finish;
326         }
327
328         fprintf(f,
329                 "ROUTER=%s\n", string);
330
331         r = sd_dhcp_lease_get_netmask(lease, &address);
332         if (r < 0)
333                 goto finish;
334
335         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
336         if (!string) {
337                 r = -errno;
338                 goto finish;
339         }
340
341         fprintf(f,
342                 "NETMASK=%s\n", string);
343
344         r = sd_dhcp_lease_get_server_identifier(lease, &address);
345         if (r >= 0) {
346                 string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
347                 if (!string) {
348                         r = -errno;
349                         goto finish;
350                 }
351
352                 fprintf(f,
353                         "SERVER_ADDRESS=%s\n", string);
354         }
355
356         r = sd_dhcp_lease_get_next_server(lease, &address);
357         if (r >= 0) {
358                 string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
359                 if (!string) {
360                         r = -errno;
361                         goto finish;
362                 }
363
364                 fprintf(f,
365                         "NEXT_SERVER=%s\n", string);
366         }
367
368         r = sd_dhcp_lease_get_mtu(lease, &mtu);
369         if (r >= 0)
370                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
371
372 /* TODO: DNS. See resolv.conf writing in network-manager.c */
373
374         r = sd_dhcp_lease_get_domainname(lease, &string);
375         if (r >= 0)
376                 fprintf(f, "DOMAINNAME=%s\n", string);
377
378         r = sd_dhcp_lease_get_hostname(lease, &string);
379         if (r >= 0)
380                 fprintf(f, "HOSTNAME=%s\n", string);
381
382         r = sd_dhcp_lease_get_root_path(lease, &string);
383         if (r >= 0)
384                 fprintf(f, "ROOT_PATH=%s\n", string);
385
386         r = 0;
387
388         fflush(f);
389
390         if (ferror(f) || rename(temp_path, lease_file) < 0) {
391                 r = -errno;
392                 unlink(lease_file);
393                 unlink(temp_path);
394         }
395
396 finish:
397         if (r < 0)
398                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
399
400         return r;
401 }
402
403 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
404         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
405         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
406                             *server_address = NULL, *next_server = NULL,
407                             *mtu = NULL;
408         struct in_addr addr;
409         int r;
410
411         assert(lease_file);
412         assert(ret);
413
414         r = dhcp_lease_new(&lease);
415         if (r < 0)
416                 return r;
417
418         r = parse_env_file(lease_file, NEWLINE,
419                            "ADDRESS", &address,
420                            "ROUTER", &router,
421                            "NETMASK", &netmask,
422                            "SERVER_IDENTIFIER", &server_address,
423                            "NEXT_SERVER", &next_server,
424                            "MTU", &mtu,
425                            "DOMAINNAME", &lease->domainname,
426                            "HOSTNAME", &lease->hostname,
427                            "ROOT_PATH", &lease->root_path,
428                            NULL);
429         if (r < 0) {
430                 if (r == -ENOENT)
431                         return 0;
432
433                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
434                 return r;
435         }
436
437         r = inet_pton(AF_INET, address, &addr);
438         if (r < 0)
439                 return r;
440
441         lease->address = addr.s_addr;
442
443         r = inet_pton(AF_INET, router, &addr);
444         if (r < 0)
445                 return r;
446
447         lease->router = addr.s_addr;
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 (mtu) {
472                 uint16_t u;
473                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
474                         lease->mtu = u;
475         }
476
477         *ret = lease;
478         lease = NULL;
479
480         return 0;
481 }
482
483 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
484         uint32_t address;
485
486         assert(lease);
487         assert(lease->address != INADDR_ANY);
488
489         address = be32toh(lease->address);
490
491         /* fall back to the default subnet masks based on address class */
492
493         if ((address >> 31) == 0x0)
494                 /* class A, leading bits: 0 */
495                 lease->subnet_mask = htobe32(0xff000000);
496         else if ((address >> 30) == 0x2)
497                 /* class B, leading bits 10 */
498                 lease->subnet_mask = htobe32(0xffff0000);
499         else if ((address >> 29) == 0x6)
500                 /* class C, leading bits 110 */
501                 lease->subnet_mask = htobe32(0xffffff00);
502         else
503                 /* class D or E, no default mask. give up */
504                 return -ERANGE;
505
506         return 0;
507 }