chiark / gitweb /
sd-network: remove redundant array size parameter from functions that return arrays
[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 #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_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
51         assert_return(lease, -EINVAL);
52         assert_return(lease, -EINVAL);
53
54         *lifetime = lease->lifetime;
55
56         return 0;
57 }
58
59 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
60         assert_return(lease, -EINVAL);
61         assert_return(mtu, -EINVAL);
62
63         if (lease->mtu)
64                 *mtu = lease->mtu;
65         else
66                 return -ENOENT;
67
68         return 0;
69 }
70
71 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
72         assert_return(lease, -EINVAL);
73         assert_return(addr, -EINVAL);
74
75         if (lease->dns_size) {
76                 *addr = lease->dns;
77                 return lease->dns_size;
78         } else
79                 return -ENOENT;
80
81         return 0;
82 }
83
84 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
85         assert_return(lease, -EINVAL);
86         assert_return(addr, -EINVAL);
87
88         if (lease->ntp_size) {
89                 *addr = lease->ntp;
90                 return lease->ntp_size;
91         } else
92                 return -ENOENT;
93
94         return 0;
95 }
96
97 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
98         assert_return(lease, -EINVAL);
99         assert_return(domainname, -EINVAL);
100
101         if (lease->domainname)
102                 *domainname = lease->domainname;
103         else
104                 return -ENOENT;
105
106         return 0;
107 }
108
109 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) {
110         assert_return(lease, -EINVAL);
111         assert_return(hostname, -EINVAL);
112
113         if (lease->hostname)
114                 *hostname = lease->hostname;
115         else
116                 return -ENOENT;
117
118         return 0;
119 }
120
121 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) {
122         assert_return(lease, -EINVAL);
123         assert_return(root_path, -EINVAL);
124
125         if (lease->root_path)
126                 *root_path = lease->root_path;
127         else
128                 return -ENOENT;
129
130         return 0;
131 }
132
133 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) {
134         assert_return(lease, -EINVAL);
135         assert_return(addr, -EINVAL);
136
137         if (lease->router != INADDR_ANY)
138                 addr->s_addr = lease->router;
139         else
140                 return -ENOENT;
141
142         return 0;
143 }
144
145 int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
146         assert_return(lease, -EINVAL);
147         assert_return(addr, -EINVAL);
148
149         addr->s_addr = lease->subnet_mask;
150
151         return 0;
152 }
153
154 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
155         assert_return(lease, -EINVAL);
156         assert_return(addr, -EINVAL);
157
158         addr->s_addr = lease->server_address;
159
160         return 0;
161 }
162
163 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
164         assert_return(lease, -EINVAL);
165         assert_return(addr, -EINVAL);
166
167         addr->s_addr = lease->next_server;
168
169         return 0;
170 }
171
172 int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) {
173
174         assert_return(lease, -EINVAL);
175         assert_return(routes, -EINVAL);
176
177         if (lease->static_route_size) {
178                 *routes = lease->static_route;
179                 return lease->static_route_size;
180         } else
181                 return -ENOENT;
182
183         return 0;
184 }
185
186 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
187         if (lease)
188                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
189
190         return lease;
191 }
192
193 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
194         if (lease && REFCNT_DEC(lease->n_ref) <= 0) {
195                 free(lease->hostname);
196                 free(lease->domainname);
197                 free(lease->dns);
198                 free(lease->ntp);
199                 free(lease->static_route);
200                 free(lease);
201         }
202
203         return NULL;
204 }
205
206 static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) {
207         be32_t val;
208
209         assert(option);
210         assert(ret);
211
212         if (len == 4) {
213                 memcpy(&val, option, 4);
214                 *ret = be32toh(val);
215
216                 if (*ret < min)
217                         *ret = min;
218         }
219 }
220
221 static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) {
222         lease_parse_u32(option, len, (uint32_t *)ret, 0);
223 }
224
225 static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) {
226         be16_t val;
227
228         assert(option);
229         assert(ret);
230
231         if (len == 2) {
232                 memcpy(&val, option, 2);
233                 *ret = be16toh(val);
234
235                 if (*ret < min)
236                         *ret = min;
237         }
238 }
239
240 static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
241         assert(option);
242         assert(ret);
243
244         if (len == 4)
245                 memcpy(ret, option, 4);
246 }
247
248 static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) {
249         assert(option);
250         assert(ret);
251
252         if (len == 1)
253                 *ret = !!(*option);
254 }
255
256 static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) {
257         assert(option);
258         assert(ret);
259
260         if (len == 1) {
261                 *ret = *option;
262
263                 if (*ret < min)
264                         *ret = min;
265         }
266 }
267
268 static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
269         assert(option);
270         assert(ret);
271
272         if (len >= 1) {
273                 char *string;
274
275                 string = strndup((const char *)option, len);
276                 if (!string)
277                         return -errno;
278
279                 free(*ret);
280                 *ret = string;
281         }
282
283         return 0;
284 }
285
286 static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) {
287         assert(option);
288         assert(ret);
289         assert(ret_size);
290
291         if (len && !(len % (4 * mult))) {
292                 size_t size;
293                 struct in_addr *addresses;
294
295                 size = len / 4;
296
297                 addresses = newdup(struct in_addr, option, size);
298                 if (!addresses)
299                         return -ENOMEM;
300
301                 free(*ret);
302                 *ret = addresses;
303                 *ret_size = size;
304         }
305
306         return 0;
307 }
308
309 static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
310         return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1);
311 }
312
313 static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) {
314         return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
315 }
316
317 static int class_prefixlen(uint8_t msb_octet, uint8_t *ret) {
318         if (msb_octet < 128)
319                 /* Class A */
320                 *ret = 8;
321         else if (msb_octet < 192)
322                 /* Class B */
323                 *ret = 16;
324         else if (msb_octet < 224)
325                 /* Class C */
326                 *ret = 24;
327         else
328                 /* Class D or E -- no subnet mask */
329                 return -ERANGE;
330
331         return 0;
332 }
333
334 static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
335         size_t *routes_size, size_t *routes_allocated) {
336
337         struct in_addr addr;
338
339         assert(option);
340         assert(routes);
341         assert(routes_size);
342         assert(routes_allocated);
343
344         if (!len)
345                 return 0;
346
347         if (len % 8 != 0)
348                 return -EINVAL;
349
350         if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
351                 return -ENOMEM;
352
353         while (len >= 8) {
354                 struct sd_dhcp_route *route = *routes + *routes_size;
355
356                 if (class_prefixlen(*option, &route->dst_prefixlen) < 0) {
357                         log_error("Failed to determine destination prefix length from class based IP, ignoring");
358                         continue;
359                 }
360
361                 lease_parse_be32(option, 4, &addr.s_addr);
362                 route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
363                 option += 4;
364
365                 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
366                 option += 4;
367
368                 len -= 8;
369                 (*routes_size)++;
370         }
371
372         return 0;
373 }
374
375 /* parses RFC3442 Classless Static Route Option */
376 static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
377         size_t *routes_size, size_t *routes_allocated) {
378
379         assert(option);
380         assert(routes);
381         assert(routes_size);
382         assert(routes_allocated);
383
384         /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)*  */
385
386         while (len > 0) {
387                 uint8_t dst_octets;
388                 struct sd_dhcp_route *route;
389
390                 if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
391                     return -ENOMEM;
392
393                 route = *routes + *routes_size;
394
395                 dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
396                 route->dst_prefixlen = *option;
397                 option++;
398                 len--;
399
400                 /* can't have more than 4 octets in IPv4 */
401                 if (dst_octets > 4 || len < dst_octets)
402                         return -EINVAL;
403
404                 route->dst_addr.s_addr = 0;
405                 memcpy(&route->dst_addr.s_addr, option, dst_octets);
406                 option += dst_octets;
407                 len -= dst_octets;
408
409                 if (len < 4)
410                         return -EINVAL;
411
412                 lease_parse_be32(option, 4, &route->gw_addr.s_addr);
413                 option += 4;
414                 len -= 4;
415
416                 (*routes_size)++;
417         }
418
419         return 0;
420 }
421
422 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
423                               void *user_data) {
424         sd_dhcp_lease *lease = user_data;
425         int r;
426
427         assert(lease);
428
429         switch(code) {
430
431         case DHCP_OPTION_TIME_OFFSET:
432                 lease_parse_s32(option, len, &lease->time_offset);
433
434                 break;
435
436         case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT:
437                 lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0);
438
439                 break;
440
441         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
442                 lease_parse_u32(option, len, &lease->lifetime, 1);
443
444                 break;
445
446         case DHCP_OPTION_SERVER_IDENTIFIER:
447                 lease_parse_be32(option, len, &lease->server_address);
448
449                 break;
450
451         case DHCP_OPTION_SUBNET_MASK:
452                 lease_parse_be32(option, len, &lease->subnet_mask);
453
454                 break;
455
456         case DHCP_OPTION_BROADCAST:
457                 lease_parse_be32(option, len, &lease->broadcast);
458
459                 break;
460
461         case DHCP_OPTION_ROUTER:
462                 lease_parse_be32(option, len, &lease->router);
463
464                 break;
465
466         case DHCP_OPTION_DOMAIN_NAME_SERVER:
467                 r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
468                 if (r < 0)
469                         return r;
470
471                 break;
472
473         case DHCP_OPTION_NTP_SERVER:
474                 r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
475                 if (r < 0)
476                         return r;
477
478                 break;
479
480         case DHCP_OPTION_POLICY_FILTER:
481                 r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size);
482                 if (r < 0)
483                         return r;
484
485                 break;
486
487         case DHCP_OPTION_STATIC_ROUTE:
488                 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
489                         &lease->static_route_allocated);
490                 if (r < 0)
491                         return r;
492
493                 break;
494
495         case DHCP_OPTION_INTERFACE_MTU:
496                 lease_parse_u16(option, len, &lease->mtu, 68);
497
498                 break;
499
500         case DHCP_OPTION_INTERFACE_MDR:
501                 lease_parse_u16(option, len, &lease->mdr, 576);
502
503                 break;
504
505         case DHCP_OPTION_INTERFACE_TTL:
506                 lease_parse_u8(option, len, &lease->ttl, 1);
507
508                 break;
509
510         case DHCP_OPTION_BOOT_FILE_SIZE:
511                 lease_parse_u16(option, len, &lease->boot_file_size, 0);
512
513                 break;
514
515         case DHCP_OPTION_DOMAIN_NAME:
516                 r = lease_parse_string(option, len, &lease->domainname);
517                 if (r < 0)
518                         return r;
519
520                 break;
521
522         case DHCP_OPTION_HOST_NAME:
523                 r = lease_parse_string(option, len, &lease->hostname);
524                 if (r < 0)
525                         return r;
526
527                 break;
528
529         case DHCP_OPTION_ROOT_PATH:
530                 r = lease_parse_string(option, len, &lease->root_path);
531                 if (r < 0)
532                         return r;
533
534                 break;
535
536         case DHCP_OPTION_RENEWAL_T1_TIME:
537                 lease_parse_u32(option, len, &lease->t1, 1);
538
539                 break;
540
541         case DHCP_OPTION_REBINDING_T2_TIME:
542                 lease_parse_u32(option, len, &lease->t2, 1);
543
544                 break;
545
546         case DHCP_OPTION_ENABLE_IP_FORWARDING:
547                 lease_parse_bool(option, len, &lease->ip_forward);
548
549                 break;
550
551         case DHCP_OPTION_ENABLE_IP_FORWARDING_NL:
552                 lease_parse_bool(option, len, &lease->ip_forward_non_local);
553
554                 break;
555
556         case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
557                 r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
558                         &lease->static_route_allocated);
559                 if (r < 0)
560                         return r;
561
562                 break;
563         }
564
565         return 0;
566 }
567
568 int dhcp_lease_new(sd_dhcp_lease **ret) {
569         sd_dhcp_lease *lease;
570
571         lease = new0(sd_dhcp_lease, 1);
572         if (!lease)
573                 return -ENOMEM;
574
575         lease->router = INADDR_ANY;
576         lease->n_ref = REFCNT_INIT;
577
578         *ret = lease;
579         return 0;
580 }
581
582 int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
583         _cleanup_free_ char *temp_path = NULL;
584         _cleanup_fclose_ FILE *f = NULL;
585         struct in_addr address;
586         const struct in_addr *addresses;
587         const char *string;
588         uint16_t mtu;
589         struct sd_dhcp_route *routes;
590         int r;
591
592         assert(lease);
593         assert(lease_file);
594
595         r = fopen_temporary(lease_file, &f, &temp_path);
596         if (r < 0)
597                 goto finish;
598
599         fchmod(fileno(f), 0644);
600
601         r = sd_dhcp_lease_get_address(lease, &address);
602         if (r < 0)
603                 goto finish;
604
605         fprintf(f,
606                 "# This is private data. Do not parse.\n"
607                 "ADDRESS=%s\n", inet_ntoa(address));
608
609         r = sd_dhcp_lease_get_netmask(lease, &address);
610         if (r < 0)
611                 goto finish;
612
613         fprintf(f, "NETMASK=%s\n", inet_ntoa(address));
614
615         r = sd_dhcp_lease_get_router(lease, &address);
616         if (r >= 0)
617                 fprintf(f, "ROUTER=%s\n", inet_ntoa(address));
618
619         r = sd_dhcp_lease_get_server_identifier(lease, &address);
620         if (r >= 0)
621                 fprintf(f, "SERVER_ADDRESS=%s\n",
622                         inet_ntoa(address));
623
624         r = sd_dhcp_lease_get_next_server(lease, &address);
625         if (r >= 0)
626                 fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address));
627
628         r = sd_dhcp_lease_get_mtu(lease, &mtu);
629         if (r >= 0)
630                 fprintf(f, "MTU=%" PRIu16 "\n", mtu);
631
632         r = sd_dhcp_lease_get_dns(lease, &addresses);
633         if (r >= 0)
634                 serialize_in_addrs(f, "DNS", addresses, r);
635
636         r = sd_dhcp_lease_get_ntp(lease, &addresses);
637         if (r >= 0)
638                 serialize_in_addrs(f, "NTP", addresses, r);
639
640         r = sd_dhcp_lease_get_domainname(lease, &string);
641         if (r >= 0)
642                 fprintf(f, "DOMAINNAME=%s\n", string);
643
644         r = sd_dhcp_lease_get_hostname(lease, &string);
645         if (r >= 0)
646                 fprintf(f, "HOSTNAME=%s\n", string);
647
648         r = sd_dhcp_lease_get_root_path(lease, &string);
649         if (r >= 0)
650                 fprintf(f, "ROOT_PATH=%s\n", string);
651
652         r = sd_dhcp_lease_get_routes(lease, &routes);
653         if (r >= 0)
654                 serialize_dhcp_routes(f, "ROUTES", routes, r);
655
656         r = 0;
657
658         fflush(f);
659
660         if (ferror(f) || rename(temp_path, lease_file) < 0) {
661                 r = -errno;
662                 unlink(lease_file);
663                 unlink(temp_path);
664         }
665
666 finish:
667         if (r < 0)
668                 log_error("Failed to save lease data %s: %s", lease_file, strerror(-r));
669
670         return r;
671 }
672
673 int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
674         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
675         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
676                             *server_address = NULL, *next_server = NULL,
677                             *dns = NULL, *ntp = NULL, *mtu = NULL, *routes = NULL;
678         struct in_addr addr;
679         int r;
680
681         assert(lease_file);
682         assert(ret);
683
684         r = dhcp_lease_new(&lease);
685         if (r < 0)
686                 return r;
687
688         r = parse_env_file(lease_file, NEWLINE,
689                            "ADDRESS", &address,
690                            "ROUTER", &router,
691                            "NETMASK", &netmask,
692                            "SERVER_IDENTIFIER", &server_address,
693                            "NEXT_SERVER", &next_server,
694                            "DNS", &dns,
695                            "NTP", &ntp,
696                            "MTU", &mtu,
697                            "DOMAINNAME", &lease->domainname,
698                            "HOSTNAME", &lease->hostname,
699                            "ROOT_PATH", &lease->root_path,
700                            "ROUTES", &routes,
701                            NULL);
702         if (r < 0) {
703                 if (r == -ENOENT)
704                         return 0;
705
706                 log_error("Failed to read %s: %s", lease_file, strerror(-r));
707                 return r;
708         }
709
710         r = inet_pton(AF_INET, address, &addr);
711         if (r < 0)
712                 return r;
713
714         lease->address = addr.s_addr;
715
716         if (router) {
717                 r = inet_pton(AF_INET, router, &addr);
718                 if (r < 0)
719                         return r;
720
721                 lease->router = addr.s_addr;
722         }
723
724         r = inet_pton(AF_INET, netmask, &addr);
725         if (r < 0)
726                 return r;
727
728         lease->subnet_mask = addr.s_addr;
729
730         if (server_address) {
731                 r = inet_pton(AF_INET, server_address, &addr);
732                 if (r < 0)
733                         return r;
734
735                 lease->server_address = addr.s_addr;
736         }
737
738         if (next_server) {
739                 r = inet_pton(AF_INET, next_server, &addr);
740                 if (r < 0)
741                         return r;
742
743                 lease->next_server = addr.s_addr;
744         }
745
746         if (dns) {
747                 r = deserialize_in_addrs(&lease->dns, dns);
748                 if (r < 0)
749                         return r;
750
751                 lease->dns_size = r;
752         }
753
754         if (ntp) {
755                 r = deserialize_in_addrs(&lease->ntp, ntp);
756                 if (r < 0)
757                         return r;
758
759                 lease->ntp_size = r;
760         }
761
762         if (mtu) {
763                 uint16_t u;
764                 if (sscanf(mtu, "%" SCNu16, &u) > 0)
765                         lease->mtu = u;
766         }
767
768         if (routes) {
769                 r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
770                                 &lease->static_route_allocated, routes);
771                 if (r < 0)
772                     return r;
773         }
774
775         *ret = lease;
776         lease = NULL;
777
778         return 0;
779 }
780
781 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
782         uint32_t address;
783
784         assert(lease);
785         assert(lease->address != INADDR_ANY);
786
787         address = be32toh(lease->address);
788
789         /* fall back to the default subnet masks based on address class */
790
791         if ((address >> 31) == 0x0)
792                 /* class A, leading bits: 0 */
793                 lease->subnet_mask = htobe32(0xff000000);
794         else if ((address >> 30) == 0x2)
795                 /* class B, leading bits 10 */
796                 lease->subnet_mask = htobe32(0xffff0000);
797         else if ((address >> 29) == 0x6)
798                 /* class C, leading bits 110 */
799                 lease->subnet_mask = htobe32(0xffffff00);
800         else
801                 /* class D or E, no default mask. give up */
802                 return -ERANGE;
803
804         return 0;
805 }