chiark / gitweb /
sd-dhcp6-client: properly calculate buffer size when parsing options
[elogind.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 Intel Corporation. All rights reserved.
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25
26 #include "udev.h"
27 #include "udev-util.h"
28 #include "virt.h"
29 #include "siphash24.h"
30 #include "util.h"
31 #include "refcnt.h"
32
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
38
39 #define SYSTEMD_PEN 43793
40 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
41
42 struct sd_dhcp6_client {
43         RefCount n_ref;
44
45         enum DHCP6State state;
46         sd_event *event;
47         int event_priority;
48         int index;
49         struct ether_addr mac_addr;
50         DHCP6IA ia_na;
51         be32_t transaction_id;
52         struct sd_dhcp6_lease *lease;
53         int fd;
54         be16_t *req_opts;
55         size_t req_opts_allocated;
56         size_t req_opts_len;
57         sd_event_source *receive_message;
58         usec_t retransmit_time;
59         uint8_t retransmit_count;
60         sd_event_source *timeout_resend;
61         sd_event_source *timeout_resend_expire;
62         sd_dhcp6_client_cb_t cb;
63         void *userdata;
64
65         struct duid_en {
66                 uint16_t type; /* DHCP6_DUID_EN */
67                 uint32_t pen;
68                 uint8_t id[8];
69         } _packed_ duid;
70 };
71
72 static const uint16_t default_req_opts[] = {
73         DHCP6_OPTION_DNS_SERVERS,
74         DHCP6_OPTION_DOMAIN_LIST,
75         DHCP6_OPTION_NTP_SERVER,
76 };
77
78 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
79         [DHCP6_SOLICIT] = "SOLICIT",
80         [DHCP6_ADVERTISE] = "ADVERTISE",
81         [DHCP6_REQUEST] = "REQUEST",
82         [DHCP6_CONFIRM] = "CONFIRM",
83         [DHCP6_RENEW] = "RENEW",
84         [DHCP6_REBIND] = "REBIND",
85         [DHCP6_REPLY] = "REPLY",
86         [DHCP6_RELEASE] = "RELEASE",
87         [DHCP6_DECLINE] = "DECLINE",
88         [DHCP6_RECONFIGURE] = "RECONFIGURE",
89         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
90         [DHCP6_RELAY_FORW] = "RELAY-FORW",
91         [DHCP6_RELAY_REPL] = "RELAY-REPL",
92 };
93
94 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
95
96 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
97         [DHCP6_STATUS_SUCCESS] = "Success",
98         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
99         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
100         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
101         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
102         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
103 };
104
105 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
106
107 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
108 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111         _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112
113 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
114
115 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
116                                  sd_dhcp6_client_cb_t cb, void *userdata)
117 {
118         assert_return(client, -EINVAL);
119
120         client->cb = cb;
121         client->userdata = userdata;
122
123         return 0;
124 }
125
126 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
127 {
128         assert_return(client, -EINVAL);
129         assert_return(interface_index >= -1, -EINVAL);
130
131         client->index = interface_index;
132
133         return 0;
134 }
135
136 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
137                             const struct ether_addr *mac_addr)
138 {
139         assert_return(client, -EINVAL);
140
141         if (mac_addr)
142                 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
143         else
144                 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
145
146         return 0;
147 }
148
149 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
150                                        uint16_t option) {
151         size_t t;
152
153         assert_return(client, -EINVAL);
154         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
155
156         switch(option) {
157         case DHCP6_OPTION_DNS_SERVERS:
158         case DHCP6_OPTION_DOMAIN_LIST:
159         case DHCP6_OPTION_SNTP_SERVERS:
160         case DHCP6_OPTION_NTP_SERVER:
161                 break;
162
163         default:
164                 return -EINVAL;
165         }
166
167         for (t = 0; t < client->req_opts_len; t++)
168                 if (client->req_opts[t] == htobe16(option))
169                         return -EEXIST;
170
171         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
172                             client->req_opts_len + 1))
173                 return -ENOMEM;
174
175         client->req_opts[client->req_opts_len++] = htobe16(option);
176
177         return 0;
178 }
179
180 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
181         assert_return(client, -EINVAL);
182         assert_return(ret, -EINVAL);
183
184         if (!client->lease)
185                 return -ENOMSG;
186
187         *ret = sd_dhcp6_lease_ref(client->lease);
188
189         return 0;
190 }
191
192 static void client_notify(sd_dhcp6_client *client, int event) {
193         if (client->cb)
194                 client->cb(client, event, client->userdata);
195 }
196
197 static int client_reset(sd_dhcp6_client *client) {
198         assert_return(client, -EINVAL);
199
200         client->receive_message =
201                 sd_event_source_unref(client->receive_message);
202
203         client->fd = safe_close(client->fd);
204
205         client->transaction_id = 0;
206
207         client->ia_na.timeout_t1 =
208                 sd_event_source_unref(client->ia_na.timeout_t1);
209         client->ia_na.timeout_t2 =
210                 sd_event_source_unref(client->ia_na.timeout_t2);
211
212         client->retransmit_time = 0;
213         client->retransmit_count = 0;
214         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215         client->timeout_resend_expire =
216                 sd_event_source_unref(client->timeout_resend_expire);
217
218         client->state = DHCP6_STATE_STOPPED;
219
220         return 0;
221 }
222
223 static void client_stop(sd_dhcp6_client *client, int error) {
224         DHCP6_CLIENT_DONT_DESTROY(client);
225
226         assert(client);
227
228         client_notify(client, error);
229
230         client_reset(client);
231 }
232
233 static int client_send_message(sd_dhcp6_client *client) {
234         _cleanup_free_ DHCP6Message *message = NULL;
235         struct in6_addr all_servers =
236                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
237         size_t len, optlen = 512;
238         uint8_t *opt;
239         int r;
240
241         len = sizeof(DHCP6Message) + optlen;
242
243         message = malloc0(len);
244         if (!message)
245                 return -ENOMEM;
246
247         opt = (uint8_t *)(message + 1);
248
249         message->transaction_id = client->transaction_id;
250
251         switch(client->state) {
252         case DHCP6_STATE_SOLICITATION:
253                 message->type = DHCP6_SOLICIT;
254
255                 r = dhcp6_option_append(&opt, &optlen,
256                                         DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
257                 if (r < 0)
258                         return r;
259
260                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
261                 if (r < 0)
262                         return r;
263
264                 break;
265
266         case DHCP6_STATE_REQUEST:
267         case DHCP6_STATE_RENEW:
268
269                 if (client->state == DHCP6_STATE_REQUEST)
270                         message->type = DHCP6_REQUEST;
271                 else
272                         message->type = DHCP6_RENEW;
273
274                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
275                                         client->lease->serverid_len,
276                                         client->lease->serverid);
277                 if (r < 0)
278                         return r;
279
280                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
281                 if (r < 0)
282                         return r;
283
284                 break;
285
286         case DHCP6_STATE_REBIND:
287                 message->type = DHCP6_REBIND;
288
289                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290                 if (r < 0)
291                         return r;
292
293                 break;
294
295         case DHCP6_STATE_STOPPED:
296         case DHCP6_STATE_BOUND:
297                 return -EINVAL;
298         }
299
300         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
301                                 client->req_opts_len * sizeof(be16_t),
302                                 client->req_opts);
303         if (r < 0)
304                 return r;
305
306         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
307                                 sizeof(client->duid), &client->duid);
308         if (r < 0)
309                 return r;
310
311         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
312                                           len - optlen);
313         if (r < 0)
314                 return r;
315
316         log_dhcp6_client(client, "Sent %s",
317                          dhcp6_message_type_to_string(message->type));
318
319         return 0;
320 }
321
322 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
323                              void *userdata) {
324         sd_dhcp6_client *client = userdata;
325
326         assert_return(s, -EINVAL);
327         assert_return(client, -EINVAL);
328         assert_return(client->lease, -EINVAL);
329
330         client->lease->ia.timeout_t2 =
331                 sd_event_source_unref(client->lease->ia.timeout_t2);
332
333         log_dhcp6_client(client, "Timeout T2");
334
335         client_start(client, DHCP6_STATE_REBIND);
336
337         return 0;
338 }
339
340 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
341                              void *userdata) {
342         sd_dhcp6_client *client = userdata;
343
344         assert_return(s, -EINVAL);
345         assert_return(client, -EINVAL);
346         assert_return(client->lease, -EINVAL);
347
348         client->lease->ia.timeout_t1 =
349                 sd_event_source_unref(client->lease->ia.timeout_t1);
350
351         log_dhcp6_client(client, "Timeout T1");
352
353         client_start(client, DHCP6_STATE_RENEW);
354
355         return 0;
356 }
357
358 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
359                                         void *userdata) {
360         sd_dhcp6_client *client = userdata;
361         DHCP6_CLIENT_DONT_DESTROY(client);
362         enum DHCP6State state;
363
364         assert(s);
365         assert(client);
366         assert(client->event);
367
368         state = client->state;
369
370         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
371
372         /* RFC 3315, section 18.1.4., says that "...the client may choose to
373            use a Solicit message to locate a new DHCP server..." */
374         if (state == DHCP6_STATE_REBIND)
375                 client_start(client, DHCP6_STATE_SOLICITATION);
376
377         return 0;
378 }
379
380 static usec_t client_timeout_compute_random(usec_t val) {
381         return val - val / 10 +
382                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
383 }
384
385 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
386                                  void *userdata) {
387         int r = 0;
388         sd_dhcp6_client *client = userdata;
389         usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
390         usec_t max_retransmit_duration = 0;
391         uint8_t max_retransmit_count = 0;
392         char time_string[FORMAT_TIMESPAN_MAX];
393         uint32_t expire = 0;
394
395         assert(s);
396         assert(client);
397         assert(client->event);
398
399         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
400
401         switch (client->state) {
402         case DHCP6_STATE_SOLICITATION:
403
404                 if (client->retransmit_count && client->lease) {
405                         client_start(client, DHCP6_STATE_REQUEST);
406                         return 0;
407                 }
408
409                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
410                 max_retransmit_time = DHCP6_SOL_MAX_RT;
411
412                 break;
413
414         case DHCP6_STATE_REQUEST:
415                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
416                 max_retransmit_time = DHCP6_REQ_MAX_RT;
417                 max_retransmit_count = DHCP6_REQ_MAX_RC;
418
419                 break;
420
421         case DHCP6_STATE_RENEW:
422                 init_retransmit_time = DHCP6_REN_TIMEOUT;
423                 max_retransmit_time = DHCP6_REN_MAX_RT;
424
425                 /* RFC 3315, section 18.1.3. says max retransmit duration will
426                    be the remaining time until T2. Instead of setting MRD,
427                    wait for T2 to trigger with the same end result */
428
429                 break;
430
431         case DHCP6_STATE_REBIND:
432                 init_retransmit_time = DHCP6_REB_TIMEOUT;
433                 max_retransmit_time = DHCP6_REB_MAX_RT;
434
435                 if (!client->timeout_resend_expire) {
436                         r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
437                                                          &expire);
438                         if (r < 0) {
439                                 client_stop(client, r);
440                                 return 0;
441                         }
442                         max_retransmit_duration = expire * USEC_PER_SEC;
443                 }
444
445                 break;
446
447         case DHCP6_STATE_STOPPED:
448         case DHCP6_STATE_BOUND:
449                 return 0;
450         }
451
452         if (max_retransmit_count &&
453             client->retransmit_count >= max_retransmit_count) {
454                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
455                 return 0;
456         }
457
458         r = client_send_message(client);
459         if (r >= 0)
460                 client->retransmit_count++;
461
462
463         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
464         if (r < 0)
465                 goto error;
466
467         if (!client->retransmit_time) {
468                 client->retransmit_time =
469                         client_timeout_compute_random(init_retransmit_time);
470
471                 if (client->state == DHCP6_STATE_SOLICITATION)
472                         client->retransmit_time += init_retransmit_time / 10;
473
474         } else {
475                 if (max_retransmit_time &&
476                     client->retransmit_time > max_retransmit_time / 2)
477                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
478                 else
479                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
480         }
481
482         log_dhcp6_client(client, "Next retransmission in %s",
483                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
484                                          client->retransmit_time, 0));
485
486         r = sd_event_add_time(client->event, &client->timeout_resend,
487                               clock_boottime_or_monotonic(),
488                               time_now + client->retransmit_time,
489                               10 * USEC_PER_MSEC, client_timeout_resend,
490                               client);
491         if (r < 0)
492                 goto error;
493
494         r = sd_event_source_set_priority(client->timeout_resend,
495                                          client->event_priority);
496         if (r < 0)
497                 goto error;
498
499         r = sd_event_source_set_name(client->timeout_resend,
500                                      "dhcp6-resend-timer");
501         if (r < 0)
502                 goto error;
503
504         if (max_retransmit_duration && !client->timeout_resend_expire) {
505
506                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
507                                  max_retransmit_duration / USEC_PER_SEC);
508
509                 r = sd_event_add_time(client->event,
510                                       &client->timeout_resend_expire,
511                                       clock_boottime_or_monotonic(),
512                                       time_now + max_retransmit_duration,
513                                       USEC_PER_SEC,
514                                       client_timeout_resend_expire, client);
515                 if (r < 0)
516                         goto error;
517
518                 r = sd_event_source_set_priority(client->timeout_resend_expire,
519                                                  client->event_priority);
520                 if (r < 0)
521                         goto error;
522
523                 r = sd_event_source_set_name(client->timeout_resend_expire,
524                                              "dhcp6-resend-expire-timer");
525                 if (r < 0)
526                         goto error;
527         }
528
529 error:
530         if (r < 0)
531                 client_stop(client, r);
532
533         return 0;
534 }
535
536 static int client_ensure_iaid(sd_dhcp6_client *client) {
537         /* name is a pointer to memory in the udev_device struct, so must
538            have the same scope */
539         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
540         const char *name = NULL;
541         uint64_t id;
542
543         assert(client);
544
545         if (client->ia_na.id)
546                 return 0;
547
548         if (detect_container(NULL) <= 0) {
549                 /* not in a container, udev will be around */
550                 _cleanup_udev_unref_ struct udev *udev;
551                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
552
553                 udev = udev_new();
554                 if (!udev)
555                         return -ENOMEM;
556
557                 sprintf(ifindex_str, "n%d", client->index);
558                 device = udev_device_new_from_device_id(udev, ifindex_str);
559                 if (!device)
560                         return -errno;
561
562                 if (udev_device_get_is_initialized(device) <= 0)
563                         /* not yet ready */
564                         return -EBUSY;
565
566                 name = net_get_name(device);
567         }
568
569         if (name)
570                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
571         else
572                 /* fall back to mac address if no predictable name available */
573                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
574                           HASH_KEY.bytes);
575
576         /* fold into 32 bits */
577         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
578
579         return 0;
580 }
581
582 static int client_parse_message(sd_dhcp6_client *client,
583                                 DHCP6Message *message, size_t len,
584                                 sd_dhcp6_lease *lease) {
585         int r;
586         uint8_t *optval, *option, *id = NULL;
587         uint16_t optcode, status;
588         size_t optlen, id_len;
589         bool clientid = false;
590         be32_t iaid_lease;
591
592         option = (uint8_t *)message + sizeof(DHCP6Message);
593         len -= sizeof(DHCP6Message);
594
595         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
596                                        &optval)) >= 0) {
597                 switch (optcode) {
598                 case DHCP6_OPTION_CLIENTID:
599                         if (clientid) {
600                                 log_dhcp6_client(client, "%s contains multiple clientids",
601                                                  dhcp6_message_type_to_string(message->type));
602                                 return -EINVAL;
603                         }
604
605                         if (optlen != sizeof(client->duid) ||
606                             memcmp(&client->duid, optval, optlen) != 0) {
607                                 log_dhcp6_client(client, "%s DUID does not match",
608                                                  dhcp6_message_type_to_string(message->type));
609
610                                 return -EINVAL;
611                         }
612                         clientid = true;
613
614                         break;
615
616                 case DHCP6_OPTION_SERVERID:
617                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
618                         if (r >= 0 && id) {
619                                 log_dhcp6_client(client, "%s contains multiple serverids",
620                                                  dhcp6_message_type_to_string(message->type));
621                                 return -EINVAL;
622                         }
623
624                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
625                         if (r < 0)
626                                 return r;
627
628                         break;
629
630                 case DHCP6_OPTION_PREFERENCE:
631                         if (optlen != 1)
632                                 return -EINVAL;
633
634                         r = dhcp6_lease_set_preference(lease, *optval);
635                         if (r < 0)
636                                 return r;
637
638                         break;
639
640                 case DHCP6_OPTION_STATUS_CODE:
641                         if (optlen < 2)
642                                 return -EINVAL;
643
644                         status = optval[0] << 8 | optval[1];
645                         if (status) {
646                                 log_dhcp6_client(client, "%s Status %s",
647                                                  dhcp6_message_type_to_string(message->type),
648                                                  dhcp6_message_status_to_string(status));
649                                 return -EINVAL;
650                         }
651
652                         break;
653
654                 case DHCP6_OPTION_IA_NA:
655                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
656                                                   &lease->ia);
657                         if (r < 0 && r != -ENOMSG)
658                                 return r;
659
660                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
661                         if (r < 0)
662                                 return r;
663
664                         if (client->ia_na.id != iaid_lease) {
665                                 log_dhcp6_client(client, "%s has wrong IAID",
666                                                  dhcp6_message_type_to_string(message->type));
667                                 return -EINVAL;
668                         }
669
670                         break;
671
672                 case DHCP6_OPTION_RAPID_COMMIT:
673                         r = dhcp6_lease_set_rapid_commit(lease);
674                         if (r < 0)
675                                 return r;
676
677                         break;
678                 }
679         }
680
681         if ((r < 0 && r != -ENOMSG) || !clientid) {
682                 log_dhcp6_client(client, "%s has incomplete options",
683                                  dhcp6_message_type_to_string(message->type));
684                 return -EINVAL;
685         }
686
687         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
688         if (r < 0)
689                 log_dhcp6_client(client, "%s has no server id",
690                                  dhcp6_message_type_to_string(message->type));
691
692         return r;
693 }
694
695 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
696                                 size_t len)
697 {
698         int r;
699         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
700         bool rapid_commit;
701
702         if (reply->type != DHCP6_REPLY)
703                 return 0;
704
705         r = dhcp6_lease_new(&lease);
706         if (r < 0)
707                 return -ENOMEM;
708
709         r = client_parse_message(client, reply, len, lease);
710         if (r < 0)
711                 return r;
712
713         if (client->state == DHCP6_STATE_SOLICITATION) {
714                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
715                 if (r < 0)
716                         return r;
717
718                 if (!rapid_commit)
719                         return 0;
720         }
721
722         if (client->lease)
723                 dhcp6_lease_clear_timers(&client->lease->ia);
724
725         client->lease = sd_dhcp6_lease_unref(client->lease);
726         client->lease = lease;
727         lease = NULL;
728
729         return DHCP6_STATE_BOUND;
730 }
731
732 static int client_receive_advertise(sd_dhcp6_client *client,
733                                     DHCP6Message *advertise, size_t len) {
734         int r;
735         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
736         uint8_t pref_advertise = 0, pref_lease = 0;
737
738         if (advertise->type != DHCP6_ADVERTISE)
739                 return 0;
740
741         r = dhcp6_lease_new(&lease);
742         if (r < 0)
743                 return r;
744
745         r = client_parse_message(client, advertise, len, lease);
746         if (r < 0)
747                 return r;
748
749         r = dhcp6_lease_get_preference(lease, &pref_advertise);
750         if (r < 0)
751                 return r;
752
753         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
754         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
755                 sd_dhcp6_lease_unref(client->lease);
756                 client->lease = lease;
757                 lease = NULL;
758                 r = 0;
759         }
760
761         if (pref_advertise == 255 || client->retransmit_count > 1)
762                 r = DHCP6_STATE_REQUEST;
763
764         return r;
765 }
766
767 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
768                                   void *userdata) {
769         sd_dhcp6_client *client = userdata;
770         DHCP6_CLIENT_DONT_DESTROY(client);
771         _cleanup_free_ DHCP6Message *message;
772         int r, buflen, len;
773
774         assert(s);
775         assert(client);
776         assert(client->event);
777
778         r = ioctl(fd, FIONREAD, &buflen);
779         if (r < 0 || buflen <= 0)
780                 buflen = DHCP6_MIN_OPTIONS_SIZE;
781
782         message = malloc0(buflen);
783         if (!message)
784                 return -ENOMEM;
785
786         len = read(fd, message, buflen);
787         if ((size_t)len < sizeof(DHCP6Message)) {
788                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
789                 return 0;
790         }
791
792         switch(message->type) {
793         case DHCP6_SOLICIT:
794         case DHCP6_REQUEST:
795         case DHCP6_CONFIRM:
796         case DHCP6_RENEW:
797         case DHCP6_REBIND:
798         case DHCP6_RELEASE:
799         case DHCP6_DECLINE:
800         case DHCP6_INFORMATION_REQUEST:
801         case DHCP6_RELAY_FORW:
802         case DHCP6_RELAY_REPL:
803                 return 0;
804
805         case DHCP6_ADVERTISE:
806         case DHCP6_REPLY:
807         case DHCP6_RECONFIGURE:
808                 break;
809
810         default:
811                 log_dhcp6_client(client, "unknown message type %d",
812                                  message->type);
813                 return 0;
814         }
815
816         if (client->transaction_id != (message->transaction_id &
817                                        htobe32(0x00ffffff)))
818                 return 0;
819
820         switch (client->state) {
821         case DHCP6_STATE_SOLICITATION:
822                 r = client_receive_advertise(client, message, len);
823
824                 if (r == DHCP6_STATE_REQUEST) {
825                         client_start(client, r);
826
827                         break;
828                 }
829
830                 /* fall through for Soliciation Rapid Commit option check */
831         case DHCP6_STATE_REQUEST:
832         case DHCP6_STATE_RENEW:
833         case DHCP6_STATE_REBIND:
834
835                 r = client_receive_reply(client, message, len);
836                 if (r < 0)
837                         return 0;
838
839                 if (r == DHCP6_STATE_BOUND) {
840
841                         r = client_start(client, DHCP6_STATE_BOUND);
842                         if (r < 0) {
843                                 client_stop(client, r);
844                                 return 0;
845                         }
846
847                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
848                 }
849
850                 break;
851
852         case DHCP6_STATE_BOUND:
853
854                 break;
855
856         case DHCP6_STATE_STOPPED:
857                 return 0;
858         }
859
860         if (r >= 0) {
861                 log_dhcp6_client(client, "Recv %s",
862                                  dhcp6_message_type_to_string(message->type));
863         }
864
865         return 0;
866 }
867
868 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
869 {
870         int r;
871         usec_t timeout, time_now;
872         char time_string[FORMAT_TIMESPAN_MAX];
873
874         assert_return(client, -EINVAL);
875         assert_return(client->event, -EINVAL);
876         assert_return(client->index > 0, -EINVAL);
877         assert_return(client->state != state, -EINVAL);
878
879         client->timeout_resend_expire =
880                 sd_event_source_unref(client->timeout_resend_expire);
881         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
882         client->retransmit_time = 0;
883         client->retransmit_count = 0;
884
885         switch (state) {
886         case DHCP6_STATE_STOPPED:
887         case DHCP6_STATE_SOLICITATION:
888
889                 r = client_ensure_iaid(client);
890                 if (r < 0)
891                         return r;
892
893                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
894                 if (r < 0)
895                         return r;
896
897                 client->fd = r;
898
899                 r = sd_event_add_io(client->event, &client->receive_message,
900                                     client->fd, EPOLLIN, client_receive_message,
901                                     client);
902                 if (r < 0)
903                         return r;
904
905                 r = sd_event_source_set_priority(client->receive_message,
906                                                  client->event_priority);
907                 if (r < 0)
908                         return r;
909
910                 r = sd_event_source_set_name(client->receive_message,
911                                              "dhcp6-receive-message");
912                 if (r < 0)
913                         return r;
914
915                 client->state = DHCP6_STATE_SOLICITATION;
916
917                 break;
918
919         case DHCP6_STATE_REQUEST:
920         case DHCP6_STATE_RENEW:
921         case DHCP6_STATE_REBIND:
922
923                 client->state = state;
924
925                 break;
926
927         case DHCP6_STATE_BOUND:
928
929                 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
930                 if (r < 0)
931                         return r;
932
933                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
934                     client->lease->ia.lifetime_t2 == 0xffffffff) {
935
936                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
937                                          be32toh(client->lease->ia.lifetime_t1),
938                                          be32toh(client->lease->ia.lifetime_t2));
939
940                         return 0;
941                 }
942
943                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
944
945                 log_dhcp6_client(client, "T1 expires in %s",
946                                  format_timespan(time_string,
947                                                  FORMAT_TIMESPAN_MAX,
948                                                  timeout, 0));
949
950                 r = sd_event_add_time(client->event,
951                                       &client->lease->ia.timeout_t1,
952                                       clock_boottime_or_monotonic(), time_now + timeout,
953                                       10 * USEC_PER_SEC, client_timeout_t1,
954                                       client);
955                 if (r < 0)
956                         return r;
957
958                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
959                                                  client->event_priority);
960                 if (r < 0)
961                         return r;
962
963                 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
964                                              "dhcp6-t1-timeout");
965                 if (r < 0)
966                         return r;
967
968                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
969
970                 log_dhcp6_client(client, "T2 expires in %s",
971                                  format_timespan(time_string,
972                                                  FORMAT_TIMESPAN_MAX,
973                                                  timeout, 0));
974
975                 r = sd_event_add_time(client->event,
976                                       &client->lease->ia.timeout_t2,
977                                       clock_boottime_or_monotonic(), time_now + timeout,
978                                       10 * USEC_PER_SEC, client_timeout_t2,
979                                       client);
980                 if (r < 0)
981                         return r;
982
983                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
984                                                  client->event_priority);
985                 if (r < 0)
986                         return r;
987
988                 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
989                                              "dhcp6-t2-timeout");
990                 if (r < 0)
991                         return r;
992
993                 client->state = state;
994
995                 return 0;
996         }
997
998         client->transaction_id = random_u32() & htobe32(0x00ffffff);
999
1000         r = sd_event_add_time(client->event, &client->timeout_resend,
1001                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1002                               client);
1003         if (r < 0)
1004                 return r;
1005
1006         r = sd_event_source_set_priority(client->timeout_resend,
1007                                          client->event_priority);
1008         if (r < 0)
1009                 return r;
1010
1011         r = sd_event_source_set_name(client->timeout_resend,
1012                                      "dhcp6-resend-timeout");
1013         if (r < 0)
1014                 return r;
1015
1016         return 0;
1017 }
1018
1019 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1020 {
1021         client_stop(client, DHCP6_EVENT_STOP);
1022
1023         return 0;
1024 }
1025
1026 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1027 {
1028         int r = 0;
1029
1030         assert_return(client, -EINVAL);
1031         assert_return(client->event, -EINVAL);
1032         assert_return(client->index > 0, -EINVAL);
1033
1034         r = client_reset(client);
1035         if (r < 0)
1036                 return r;
1037
1038         return client_start(client, DHCP6_STATE_SOLICITATION);
1039 }
1040
1041 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1042                                  int priority)
1043 {
1044         int r;
1045
1046         assert_return(client, -EINVAL);
1047         assert_return(!client->event, -EBUSY);
1048
1049         if (event)
1050                 client->event = sd_event_ref(event);
1051         else {
1052                 r = sd_event_default(&client->event);
1053                 if (r < 0)
1054                         return 0;
1055         }
1056
1057         client->event_priority = priority;
1058
1059         return 0;
1060 }
1061
1062 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1063         assert_return(client, -EINVAL);
1064
1065         client->event = sd_event_unref(client->event);
1066
1067         return 0;
1068 }
1069
1070 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1071         if (!client)
1072                 return NULL;
1073
1074         return client->event;
1075 }
1076
1077 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1078         if (client)
1079                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1080
1081         return client;
1082 }
1083
1084 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1085         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1086                 client_reset(client);
1087
1088                 sd_dhcp6_client_detach_event(client);
1089
1090                 free(client->req_opts);
1091                 free(client);
1092
1093                 return NULL;
1094         }
1095
1096         return client;
1097 }
1098
1099 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1100 {
1101         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1102         sd_id128_t machine_id;
1103         int r;
1104         size_t t;
1105
1106         assert_return(ret, -EINVAL);
1107
1108         client = new0(sd_dhcp6_client, 1);
1109         if (!client)
1110                 return -ENOMEM;
1111
1112         client->n_ref = REFCNT_INIT;
1113
1114         client->ia_na.type = DHCP6_OPTION_IA_NA;
1115
1116         client->index = -1;
1117
1118         client->fd = -1;
1119
1120         /* initialize DUID */
1121         client->duid.type = htobe16(DHCP6_DUID_EN);
1122         client->duid.pen = htobe32(SYSTEMD_PEN);
1123
1124         r = sd_id128_get_machine(&machine_id);
1125         if (r < 0)
1126                 return r;
1127
1128         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1129            directly */
1130         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1131                   HASH_KEY.bytes);
1132
1133         client->req_opts_len = ELEMENTSOF(default_req_opts);
1134
1135         client->req_opts = new0(be16_t, client->req_opts_len);
1136         if (!client->req_opts)
1137                 return -ENOMEM;
1138
1139         for (t = 0; t < client->req_opts_len; t++)
1140                 client->req_opts[t] = htobe16(default_req_opts[t]);
1141
1142         *ret = client;
1143         client = NULL;
1144
1145         return 0;
1146 }