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