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