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