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