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