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