chiark / gitweb /
722dd0a419bae0c0f715ba2e0ecca36caad736d0
[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 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
130         if (lease)
131                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
132
133         return lease;
134 }
135
136 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
137         if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
138                 free(lease->hostname);
139                 free(lease->domainname);
140                 free(lease->dns);
141                 free(lease);
142         }
143
144         return NULL;
145 }
146
147 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
148                               void *user_data) {
149         sd_dhcp_lease *lease = user_data;
150         be32_t val;
151
152         switch(code) {
153
154         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
155                 if (len == 4) {
156                         memcpy(&val, option, 4);
157                         lease->lifetime = be32toh(val);
158                 }
159
160                 break;
161
162         case DHCP_OPTION_SERVER_IDENTIFIER:
163                 if (len >= 4)
164                         memcpy(&lease->server_address, option, 4);
165
166                 break;
167
168         case DHCP_OPTION_SUBNET_MASK:
169                 if (len >= 4)
170                         memcpy(&lease->subnet_mask, option, 4);
171
172                 break;
173
174         case DHCP_OPTION_ROUTER:
175                 if (len >= 4)
176                         memcpy(&lease->router, option, 4);
177
178                 break;
179
180         case DHCP_OPTION_DOMAIN_NAME_SERVER:
181                 if (len && !(len % 4)) {
182                         unsigned i;
183
184                         lease->dns_size = len / 4;
185
186                         free(lease->dns);
187                         lease->dns = new0(struct in_addr, lease->dns_size);
188                         if (!lease->dns)
189                                 return -ENOMEM;
190
191                         for (i = 0; i < lease->dns_size; i++) {
192                                 memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
193                         }
194                 }
195
196                 break;
197
198         case DHCP_OPTION_INTERFACE_MTU:
199                 if (len >= 2) {
200                         be16_t mtu;
201
202                         memcpy(&mtu, option, 2);
203                         lease->mtu = be16toh(mtu);
204
205                         if (lease->mtu < 68)
206                                 lease->mtu = 0;
207                 }
208
209                 break;
210
211         case DHCP_OPTION_DOMAIN_NAME:
212                 if (len >= 1) {
213                         free(lease->domainname);
214                         lease->domainname = strndup((const char *)option, len);
215                 }
216
217                 break;
218
219         case DHCP_OPTION_HOST_NAME:
220                 if (len >= 1) {
221                         free(lease->hostname);
222                         lease->hostname = strndup((const char *)option, len);
223                 }
224
225                 break;
226
227         case DHCP_OPTION_ROOT_PATH:
228                 if (len >= 1) {
229                         free(lease->root_path);
230                         lease->root_path = strndup((const char *)option, len);
231                 }
232
233                 break;
234
235         case DHCP_OPTION_RENEWAL_T1_TIME:
236                 if (len == 4) {
237                         memcpy(&val, option, 4);
238                         lease->t1 = be32toh(val);
239                 }
240
241                 break;
242
243         case DHCP_OPTION_REBINDING_T2_TIME:
244                 if (len == 4) {
245                         memcpy(&val, option, 4);
246                         lease->t2 = be32toh(val);
247                 }
248
249                 break;
250         }
251
252         return 0;
253 }
254
255 int dhcp_lease_new(sd_dhcp_lease **ret) {
256         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
257
258         lease = new0(sd_dhcp_lease, 1);
259         if (!lease)
260                 return -ENOMEM;
261
262         lease->n_ref = REFCNT_INIT;
263
264         *ret = lease;
265         lease = NULL;
266
267         return 0;
268 }
269
270 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
271         _cleanup_free_ char *temp_path = NULL;
272         _cleanup_fclose_ FILE *f = NULL;
273         char buf[INET_ADDRSTRLEN];
274         struct in_addr address;
275         const char *string;
276         uint16_t mtu;
277         int r;
278
279         assert(lease);
280         assert(lease_file);
281
282         r = mkdir_safe_label("/run/systemd/network/leases", 0755, 0, 0);
283         if (r < 0)
284                 goto finish;
285
286         r = fopen_temporary(lease_file, &f, &temp_path);
287         if (r < 0)
288                 goto finish;
289
290         fchmod(fileno(f), 0644);
291
292         r = sd_dhcp_lease_get_address(lease, &address);
293         if (r < 0)
294                 goto finish;
295
296         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
297         if (!string) {
298                 r = -errno;
299                 goto finish;
300         }
301
302         fprintf(f,
303                 "# This is private data. Do not parse.\n"
304                 "ADDRESS=%s\n", string);
305
306         r = sd_dhcp_lease_get_router(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                 "ROUTER=%s\n", string);
318
319         r = sd_dhcp_lease_get_netmask(lease, &address);
320         if (r < 0)
321                 goto finish;
322
323         string = inet_ntop(AF_INET, &address, buf, INET_ADDRSTRLEN);
324         if (!string) {
325                 r = -errno;
326                 goto finish;
327         }
328
329         fprintf(f,
330                 "NETMASK=%s\n", string);
331
332         r = sd_dhcp_lease_get_mtu(lease, &mtu);
333         if (r >= 0)
334                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
335
336 /* TODO: DNS. See resolv.conf writing in network-manager.c */
337
338         r = sd_dhcp_lease_get_domainname(lease, &string);
339         if (r >= 0)
340                 fprintf(f, "DOMAINNAME=%s\n", string);
341
342         r = sd_dhcp_lease_get_hostname(lease, &string);
343         if (r >= 0)
344                 fprintf(f, "HOSTNAME=%s\n", string);
345
346         r = sd_dhcp_lease_get_root_path(lease, &string);
347         if (r >= 0)
348                 fprintf(f, "ROOT_PATH=%s\n", string);
349
350         r = 0;
351
352         fflush(f);
353
354         if (ferror(f) || rename(temp_path, lease_file) < 0) {
355                 r = -errno;
356                 unlink(lease_file);
357                 unlink(temp_path);
358         }
359
360 finish:
361         if (r < 0)
362                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
363
364         return r;
365 }
366
367 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
368         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
369         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
370                             *mtu = NULL;
371         struct in_addr addr;
372         int r;
373
374         assert(lease_file);
375         assert(ret);
376
377         r = dhcp_lease_new(&lease);
378         if (r < 0)
379                 return r;
380
381         r = parse_env_file(lease_file, NEWLINE,
382                            "ADDRESS", &address,
383                            "ROUTER", &router,
384                            "NETMASK", &netmask,
385                            "MTU", &mtu,
386                            "DOMAINNAME", &lease->domainname,
387                            "HOSTNAME", &lease->hostname,
388                            "ROOT_PATH", &lease->root_path,
389                            NULL);
390         if (r < 0) {
391                 if (r == -ENOENT)
392                         return 0;
393
394                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
395                 return r;
396         }
397
398         r = inet_pton(AF_INET, address, &addr);
399         if (r < 0)
400                 return r;
401
402         lease->address = addr.s_addr;
403
404         r = inet_pton(AF_INET, router, &addr);
405         if (r < 0)
406                 return r;
407
408         lease->router = addr.s_addr;
409
410         r = inet_pton(AF_INET, netmask, &addr);
411         if (r < 0)
412                 return r;
413
414         lease->subnet_mask = addr.s_addr;
415
416         if (mtu) {
417                 uint16_t u;
418                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
419                         lease->mtu = u;
420         }
421
422         *ret = lease;
423         lease = NULL;
424
425         return 0;
426 }