]>
Commit | Line | Data |
---|---|---|
a6cc569e TG |
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> | |
fe8db0c5 | 26 | #include <arpa/inet.h> |
a6cc569e TG |
27 | #include <sys/param.h> |
28 | ||
29 | #include "util.h" | |
30 | #include "list.h" | |
fe8db0c5 TG |
31 | #include "mkdir.h" |
32 | #include "fileio.h" | |
a6cc569e TG |
33 | |
34 | #include "dhcp-protocol.h" | |
35 | #include "dhcp-internal.h" | |
fe8db0c5 TG |
36 | #include "dhcp-lease-internal.h" |
37 | #include "sd-dhcp-lease.h" | |
a6cc569e TG |
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 | ||
ce78df79 TG |
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 | ||
a6cc569e TG |
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 | ||
0ad853bc TG |
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 | ||
a6cc569e TG |
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 | ||
ce78df79 TG |
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 | ||
a6cc569e TG |
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 | } | |
fe8db0c5 TG |
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 | ||
0ad853bc TG |
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 | ||
fe8db0c5 TG |
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 | ||
ce78df79 TG |
367 | r = sd_dhcp_lease_get_root_path(lease, &string); |
368 | if (r >= 0) | |
369 | fprintf(f, "ROOT_PATH=%s\n", string); | |
370 | ||
fe8db0c5 TG |
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, | |
0ad853bc | 391 | *server_address = NULL, *mtu = NULL; |
fe8db0c5 TG |
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, | |
0ad853bc | 406 | "SERVER_IDENTIFIER", &server_address, |
fe8db0c5 TG |
407 | "MTU", &mtu, |
408 | "DOMAINNAME", &lease->domainname, | |
409 | "HOSTNAME", &lease->hostname, | |
ce78df79 | 410 | "ROOT_PATH", &lease->root_path, |
fe8db0c5 TG |
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 | ||
0ad853bc TG |
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 | ||
fe8db0c5 TG |
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 | } |