chiark / gitweb /
159bb502c87cac8975b510cc963313f55ae0e8ed
[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         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
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         lease = NULL;
284
285         return 0;
286 }
287
288 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
289         _cleanup_free_ char *temp_path = NULL;
290         _cleanup_fclose_ FILE *f = NULL;
291         char buf[INET_ADDRSTRLEN];
292         struct in_addr address;
293         const char *string;
294         uint16_t mtu;
295         int r;
296
297         assert(lease);
298         assert(lease_file);
299
300         r = fopen_temporary(lease_file, &f, &temp_path);
301         if (r < 0)
302                 goto finish;
303
304         fchmod(fileno(f), 0644);
305
306         r = sd_dhcp_lease_get_address(lease, &address);
307         if (r < 0)
308                 goto finish;
309
310         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
311         if (!string) {
312                 r = -errno;
313                 goto finish;
314         }
315
316         fprintf(f,
317                 "# This is private data. Do not parse.\n"
318                 "ADDRESS=%s\n", string);
319
320         r = sd_dhcp_lease_get_router(lease, &address);
321         if (r < 0)
322                 goto finish;
323
324         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
325         if (!string) {
326                 r = -errno;
327                 goto finish;
328         }
329
330         fprintf(f,
331                 "ROUTER=%s\n", string);
332
333         r = sd_dhcp_lease_get_netmask(lease, &address);
334         if (r < 0)
335                 goto finish;
336
337         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
338         if (!string) {
339                 r = -errno;
340                 goto finish;
341         }
342
343         fprintf(f,
344                 "NETMASK=%s\n", string);
345
346         r = sd_dhcp_lease_get_server_identifier(lease, &address);
347         if (r >= 0) {
348                 string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
349                 if (!string) {
350                         r = -errno;
351                         goto finish;
352                 }
353
354                 fprintf(f,
355                         "SERVER_ADDRESS=%s\n", string);
356         }
357
358         r = sd_dhcp_lease_get_next_server(lease, &address);
359         if (r >= 0) {
360                 string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
361                 if (!string) {
362                         r = -errno;
363                         goto finish;
364                 }
365
366                 fprintf(f,
367                         "NEXT_SERVER=%s\n", string);
368         }
369
370         r = sd_dhcp_lease_get_mtu(lease, &mtu);
371         if (r >= 0)
372                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
373
374 /* TODO: DNS. See resolv.conf writing in network-manager.c */
375
376         r = sd_dhcp_lease_get_domainname(lease, &string);
377         if (r >= 0)
378                 fprintf(f, "DOMAINNAME=%s\n", string);
379
380         r = sd_dhcp_lease_get_hostname(lease, &string);
381         if (r >= 0)
382                 fprintf(f, "HOSTNAME=%s\n", string);
383
384         r = sd_dhcp_lease_get_root_path(lease, &string);
385         if (r >= 0)
386                 fprintf(f, "ROOT_PATH=%s\n", string);
387
388         r = 0;
389
390         fflush(f);
391
392         if (ferror(f) || rename(temp_path, lease_file) < 0) {
393                 r = -errno;
394                 unlink(lease_file);
395                 unlink(temp_path);
396         }
397
398 finish:
399         if (r < 0)
400                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
401
402         return r;
403 }
404
405 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
406         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
407         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
408                             *server_address = NULL, *next_server = NULL,
409                             *mtu = NULL;
410         struct in_addr addr;
411         int r;
412
413         assert(lease_file);
414         assert(ret);
415
416         r = dhcp_lease_new(&lease);
417         if (r < 0)
418                 return r;
419
420         r = parse_env_file(lease_file, NEWLINE,
421                            "ADDRESS", &address,
422                            "ROUTER", &router,
423                            "NETMASK", &netmask,
424                            "SERVER_IDENTIFIER", &server_address,
425                            "NEXT_SERVER", &next_server,
426                            "MTU", &mtu,
427                            "DOMAINNAME", &lease->domainname,
428                            "HOSTNAME", &lease->hostname,
429                            "ROOT_PATH", &lease->root_path,
430                            NULL);
431         if (r < 0) {
432                 if (r == -ENOENT)
433                         return 0;
434
435                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
436                 return r;
437         }
438
439         r = inet_pton(AF_INET, address, &addr);
440         if (r < 0)
441                 return r;
442
443         lease->address = addr.s_addr;
444
445         r = inet_pton(AF_INET, router, &addr);
446         if (r < 0)
447                 return r;
448
449         lease->router = addr.s_addr;
450
451         r = inet_pton(AF_INET, netmask, &addr);
452         if (r < 0)
453                 return r;
454
455         lease->subnet_mask = addr.s_addr;
456
457         if (server_address) {
458                 r = inet_pton(AF_INET, server_address, &addr);
459                 if (r < 0)
460                         return r;
461
462                 lease->server_address = addr.s_addr;
463         }
464
465         if (next_server) {
466                 r = inet_pton(AF_INET, next_server, &addr);
467                 if (r < 0)
468                         return r;
469
470                 lease->next_server = addr.s_addr;
471         }
472
473         if (mtu) {
474                 uint16_t u;
475                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
476                         lease->mtu = u;
477         }
478
479         *ret = lease;
480         lease = NULL;
481
482         return 0;
483 }
484
485 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
486         uint32_t address;
487
488         assert(lease);
489         assert(lease->address != INADDR_ANY);
490
491         address = be32toh(lease->address);
492
493         /* fall back to the default subnet masks based on address class */
494
495         if ((address >> 31) == 0x0)
496                 /* class A, leading bits: 0 */
497                 lease->subnet_mask = htobe32(0xff000000);
498         else if ((address >> 30) == 0x2)
499                 /* class B, leading bits 10 */
500                 lease->subnet_mask = htobe32(0xffff0000);
501         else if ((address >> 29) == 0x6)
502                 /* class C, leading bits 110 */
503                 lease->subnet_mask = htobe32(0xffffff00);
504         else
505                 /* class D or E, no default mask. give up */
506                 return -ERANGE;
507
508         return 0;
509 }