chiark / gitweb /
sd-dhcp6-client: fix uninitialized variables
[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         sd_event_source *receive_message;
55         usec_t retransmit_time;
56         uint8_t retransmit_count;
57         sd_event_source *timeout_resend;
58         sd_event_source *timeout_resend_expire;
59         sd_dhcp6_client_cb_t cb;
60         void *userdata;
61
62         struct duid_en {
63                 uint16_t type; /* DHCP6_DUID_EN */
64                 uint32_t pen;
65                 uint8_t id[8];
66         } _packed_ duid;
67 };
68
69 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
70         [DHCP6_SOLICIT] = "SOLICIT",
71         [DHCP6_ADVERTISE] = "ADVERTISE",
72         [DHCP6_REQUEST] = "REQUEST",
73         [DHCP6_CONFIRM] = "CONFIRM",
74         [DHCP6_RENEW] = "RENEW",
75         [DHCP6_REBIND] = "REBIND",
76         [DHCP6_REPLY] = "REPLY",
77         [DHCP6_RELEASE] = "RELEASE",
78         [DHCP6_DECLINE] = "DECLINE",
79         [DHCP6_RECONFIGURE] = "RECONFIGURE",
80         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
81         [DHCP6_RELAY_FORW] = "RELAY-FORW",
82         [DHCP6_RELAY_REPL] = "RELAY-REPL",
83 };
84
85 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
86
87 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
88         [DHCP6_STATUS_SUCCESS] = "Success",
89         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
90         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
91         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
92         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
93         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
94 };
95
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
97
98 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
99
100 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
101                                  sd_dhcp6_client_cb_t cb, void *userdata)
102 {
103         assert_return(client, -EINVAL);
104
105         client->cb = cb;
106         client->userdata = userdata;
107
108         return 0;
109 }
110
111 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
112 {
113         assert_return(client, -EINVAL);
114         assert_return(interface_index >= -1, -EINVAL);
115
116         client->index = interface_index;
117
118         return 0;
119 }
120
121 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
122                             const struct ether_addr *mac_addr)
123 {
124         assert_return(client, -EINVAL);
125
126         if (mac_addr)
127                 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
128         else
129                 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
130
131         return 0;
132 }
133
134 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
135         assert_return(client, -EINVAL);
136         assert_return(ret, -EINVAL);
137
138         if (!client->lease)
139                 return -ENOMSG;
140
141         *ret = sd_dhcp6_lease_ref(client->lease);
142
143         return 0;
144 }
145
146 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
147         if (client->cb) {
148                 client = sd_dhcp6_client_ref(client);
149                 client->cb(client, event, client->userdata);
150                 client = sd_dhcp6_client_unref(client);
151         }
152
153         return client;
154 }
155
156 static int client_reset(sd_dhcp6_client *client) {
157         assert_return(client, -EINVAL);
158
159         client->receive_message =
160                 sd_event_source_unref(client->receive_message);
161
162         client->fd = safe_close(client->fd);
163
164         client->transaction_id = 0;
165
166         client->ia_na.timeout_t1 =
167                 sd_event_source_unref(client->ia_na.timeout_t1);
168         client->ia_na.timeout_t2 =
169                 sd_event_source_unref(client->ia_na.timeout_t2);
170
171         client->retransmit_time = 0;
172         client->retransmit_count = 0;
173         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
174         client->timeout_resend_expire =
175                 sd_event_source_unref(client->timeout_resend_expire);
176
177         client->state = DHCP6_STATE_STOPPED;
178
179         return 0;
180 }
181
182 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
183         assert_return(client, NULL);
184
185         client = client_notify(client, error);
186         if (client)
187                 client_reset(client);
188
189         return client;
190 }
191
192 static int client_send_message(sd_dhcp6_client *client) {
193         _cleanup_free_ DHCP6Message *message = NULL;
194         struct in6_addr all_servers =
195                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
196         size_t len, optlen = 512;
197         uint8_t *opt;
198         int r;
199
200         len = sizeof(DHCP6Message) + optlen;
201
202         message = malloc0(len);
203         if (!message)
204                 return -ENOMEM;
205
206         opt = (uint8_t *)(message + 1);
207
208         message->transaction_id = client->transaction_id;
209
210         switch(client->state) {
211         case DHCP6_STATE_SOLICITATION:
212                 message->type = DHCP6_SOLICIT;
213
214                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
215                 if (r < 0)
216                         return r;
217
218                 break;
219
220         case DHCP6_STATE_REQUEST:
221                 message->type = DHCP6_REQUEST;
222
223                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
224                                         client->lease->serverid_len,
225                                         client->lease->serverid);
226                 if (r < 0)
227                         return r;
228
229                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
230                 if (r < 0)
231                         return r;
232
233                 break;
234
235         case DHCP6_STATE_STOPPED:
236         case DHCP6_STATE_RS:
237         case DHCP6_STATE_BOUND:
238                 return -EINVAL;
239         }
240
241         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
242                                 sizeof(client->duid), &client->duid);
243         if (r < 0)
244                 return r;
245
246         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
247                                           len - optlen);
248         if (r < 0)
249                 return r;
250
251         log_dhcp6_client(client, "Sent %s",
252                          dhcp6_message_type_to_string(message->type));
253
254         return 0;
255 }
256
257 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
258                              void *userdata) {
259         sd_dhcp6_client *client = userdata;
260
261         assert_return(s, -EINVAL);
262         assert_return(client, -EINVAL);
263         assert_return(client->lease, -EINVAL);
264
265         client->lease->ia.timeout_t2 =
266                 sd_event_source_unref(client->lease->ia.timeout_t2);
267
268         log_dhcp6_client(client, "Timeout T2");
269
270         return 0;
271 }
272
273 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
274                              void *userdata) {
275         sd_dhcp6_client *client = userdata;
276
277         assert_return(s, -EINVAL);
278         assert_return(client, -EINVAL);
279         assert_return(client->lease, -EINVAL);
280
281         client->lease->ia.timeout_t1 =
282                 sd_event_source_unref(client->lease->ia.timeout_t1);
283
284         log_dhcp6_client(client, "Timeout T1");
285
286         return 0;
287 }
288
289 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
290                                         void *userdata) {
291         sd_dhcp6_client *client = userdata;
292
293         assert(s);
294         assert(client);
295         assert(client->event);
296
297         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
298
299         return 0;
300 }
301
302 static usec_t client_timeout_compute_random(usec_t val) {
303         return val - val / 10 +
304                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
305 }
306
307 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
308                                  void *userdata) {
309         int r = 0;
310         sd_dhcp6_client *client = userdata;
311         usec_t time_now, init_retransmit_time, max_retransmit_time;
312         usec_t max_retransmit_duration;
313         uint8_t max_retransmit_count = 0;
314         char time_string[FORMAT_TIMESPAN_MAX];
315
316         assert(s);
317         assert(client);
318         assert(client->event);
319
320         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
321
322         switch (client->state) {
323         case DHCP6_STATE_SOLICITATION:
324
325                 if (client->retransmit_count && client->lease) {
326                         client_start(client, DHCP6_STATE_REQUEST);
327                         return 0;
328                 }
329
330                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
331                 max_retransmit_time = DHCP6_SOL_MAX_RT;
332                 max_retransmit_count = 0;
333                 max_retransmit_duration = 0;
334
335                 break;
336
337         case DHCP6_STATE_REQUEST:
338                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
339                 max_retransmit_time = DHCP6_REQ_MAX_RT;
340                 max_retransmit_count = DHCP6_REQ_MAX_RC;
341                 max_retransmit_duration = 0;
342
343                 break;
344
345         case DHCP6_STATE_STOPPED:
346         case DHCP6_STATE_RS:
347         case DHCP6_STATE_BOUND:
348                 return 0;
349         }
350
351         if (max_retransmit_count &&
352             client->retransmit_count >= max_retransmit_count) {
353                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
354                 return 0;
355         }
356
357         r = client_send_message(client);
358         if (r >= 0)
359                 client->retransmit_count++;
360
361
362         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
363         if (r < 0)
364                 goto error;
365
366         if (!client->retransmit_time) {
367                 client->retransmit_time =
368                         client_timeout_compute_random(init_retransmit_time);
369
370                 if (client->state == DHCP6_STATE_SOLICITATION)
371                         client->retransmit_time += init_retransmit_time / 10;
372
373         } else {
374                 if (max_retransmit_time &&
375                     client->retransmit_time > max_retransmit_time / 2)
376                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
377                 else
378                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
379         }
380
381         log_dhcp6_client(client, "Next retransmission in %s",
382                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
383                                          client->retransmit_time, 0));
384
385         r = sd_event_add_time(client->event, &client->timeout_resend,
386                               CLOCK_MONOTONIC,
387                               time_now + client->retransmit_time,
388                               10 * USEC_PER_MSEC, client_timeout_resend,
389                               client);
390         if (r < 0)
391                 goto error;
392
393         r = sd_event_source_set_priority(client->timeout_resend,
394                                          client->event_priority);
395         if (r < 0)
396                 goto error;
397
398         if (max_retransmit_duration && !client->timeout_resend_expire) {
399
400                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
401                                  max_retransmit_duration / USEC_PER_SEC);
402
403                 r = sd_event_add_time(client->event,
404                                       &client->timeout_resend_expire,
405                                       CLOCK_MONOTONIC,
406                                       time_now + max_retransmit_duration,
407                                       USEC_PER_SEC,
408                                       client_timeout_resend_expire, client);
409                 if (r < 0)
410                         goto error;
411
412                 r = sd_event_source_set_priority(client->timeout_resend_expire,
413                                                  client->event_priority);
414                 if (r < 0)
415                         goto error;
416         }
417
418 error:
419         if (r < 0)
420                 client_stop(client, r);
421
422         return 0;
423 }
424
425 static int client_ensure_iaid(sd_dhcp6_client *client) {
426         const char *name = NULL;
427         uint64_t id;
428
429         assert(client);
430
431         if (client->ia_na.id)
432                 return 0;
433
434         if (detect_container(NULL) <= 0) {
435                 /* not in a container, udev will be around */
436                 _cleanup_udev_unref_ struct udev *udev;
437                 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
438                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
439
440                 udev = udev_new();
441                 if (!udev)
442                         return -ENOMEM;
443
444                 sprintf(ifindex_str, "n%d", client->index);
445                 device = udev_device_new_from_device_id(udev, ifindex_str);
446                 if (!device)
447                         return -errno;
448
449                 if (udev_device_get_is_initialized(device) <= 0)
450                         /* not yet ready */
451                         return -EBUSY;
452
453                 name = net_get_name(device);
454         }
455
456         if (name)
457                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
458         else
459                 /* fall back to mac address if no predictable name available */
460                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
461                           HASH_KEY.bytes);
462
463         /* fold into 32 bits */
464         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
465
466         return 0;
467 }
468
469 static int client_parse_message(sd_dhcp6_client *client,
470                                 DHCP6Message *message, size_t len,
471                                 sd_dhcp6_lease *lease) {
472         int r;
473         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
474         uint16_t optcode, status;
475         size_t optlen, id_len;
476         bool clientid = false;
477         be32_t iaid_lease;
478
479         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
480                                        &optval)) >= 0) {
481                 switch (optcode) {
482                 case DHCP6_OPTION_CLIENTID:
483                         if (clientid) {
484                                 log_dhcp6_client(client, "%s contains multiple clientids",
485                                                  dhcp6_message_type_to_string(message->type));
486                                 return -EINVAL;
487                         }
488
489                         if (optlen != sizeof(client->duid) ||
490                             memcmp(&client->duid, optval, optlen) != 0) {
491                                 log_dhcp6_client(client, "%s DUID does not match",
492                                                  dhcp6_message_type_to_string(message->type));
493
494                                 return -EINVAL;
495                         }
496                         clientid = true;
497
498                         break;
499
500                 case DHCP6_OPTION_SERVERID:
501                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
502                         if (r >= 0 && id) {
503                                 log_dhcp6_client(client, "%s contains multiple serverids",
504                                                  dhcp6_message_type_to_string(message->type));
505                                 return -EINVAL;
506                         }
507
508                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
509                         if (r < 0)
510                                 return r;
511
512                         break;
513
514                 case DHCP6_OPTION_PREFERENCE:
515                         if (optlen != 1)
516                                 return -EINVAL;
517
518                         r = dhcp6_lease_set_preference(lease, *optval);
519                         if (r < 0)
520                                 return r;
521
522                         break;
523
524                 case DHCP6_OPTION_STATUS_CODE:
525                         if (optlen < 2)
526                                 return -EINVAL;
527
528                         status = optval[0] << 8 | optval[1];
529                         if (status) {
530                                 log_dhcp6_client(client, "%s Status %s",
531                                                  dhcp6_message_type_to_string(message->type),
532                                                  dhcp6_message_status_to_string(status));
533                                 return -EINVAL;
534                         }
535
536                         break;
537
538                 case DHCP6_OPTION_IA_NA:
539                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
540                                                   &lease->ia);
541                         if (r < 0 && r != -ENOMSG)
542                                 return r;
543
544                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
545                         if (r < 0)
546                                 return r;
547
548                         if (client->ia_na.id != iaid_lease) {
549                                 log_dhcp6_client(client, "%s has wrong IAID",
550                                                  dhcp6_message_type_to_string(message->type));
551                                 return -EINVAL;
552                         }
553
554                         break;
555                 }
556         }
557
558         if ((r < 0 && r != -ENOMSG) || !clientid) {
559                 log_dhcp6_client(client, "%s has incomplete options",
560                                  dhcp6_message_type_to_string(message->type));
561                 return -EINVAL;
562         }
563
564         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
565         if (r < 0)
566                 log_dhcp6_client(client, "%s has no server id",
567                                  dhcp6_message_type_to_string(message->type));
568
569         return r;
570 }
571
572 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
573                                 size_t len)
574 {
575         int r;
576         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
577
578         if (reply->type != DHCP6_REPLY)
579                 return -EINVAL;
580
581         r = dhcp6_lease_new(&lease);
582         if (r < 0)
583                 return -ENOMEM;
584
585         r = client_parse_message(client, reply, len, lease);
586         if (r < 0)
587                 return r;
588
589         dhcp6_lease_clear_timers(&client->lease->ia);
590
591         client->lease = sd_dhcp6_lease_unref(client->lease);
592         client->lease = lease;
593         lease = NULL;
594
595         return DHCP6_STATE_BOUND;
596 }
597
598 static int client_receive_advertise(sd_dhcp6_client *client,
599                                     DHCP6Message *advertise, size_t len) {
600         int r;
601         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
602         uint8_t pref_advertise = 0, pref_lease = 0;
603
604         if (advertise->type != DHCP6_ADVERTISE)
605                 return -EINVAL;
606
607         r = dhcp6_lease_new(&lease);
608         if (r < 0)
609                 return r;
610
611         r = client_parse_message(client, advertise, len, lease);
612         if (r < 0)
613                 return r;
614
615         r = dhcp6_lease_get_preference(lease, &pref_advertise);
616         if (r < 0)
617                 return r;
618
619         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
620         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
621                 sd_dhcp6_lease_unref(client->lease);
622                 client->lease = lease;
623                 lease = NULL;
624                 r = 0;
625         }
626
627         if (pref_advertise == 255 || client->retransmit_count > 1)
628                 r = DHCP6_STATE_REQUEST;
629
630         return r;
631 }
632
633 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
634                                   void *userdata) {
635         sd_dhcp6_client *client = userdata;
636         _cleanup_free_ DHCP6Message *message;
637         int r, buflen, len;
638
639         assert(s);
640         assert(client);
641         assert(client->event);
642
643         r = ioctl(fd, FIONREAD, &buflen);
644         if (r < 0 || buflen <= 0)
645                 buflen = DHCP6_MIN_OPTIONS_SIZE;
646
647         message = malloc0(buflen);
648         if (!message)
649                 return -ENOMEM;
650
651         len = read(fd, message, buflen);
652         if ((size_t)len < sizeof(DHCP6Message)) {
653                 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
654                 return 0;
655         }
656
657         switch(message->type) {
658         case DHCP6_SOLICIT:
659         case DHCP6_REQUEST:
660         case DHCP6_CONFIRM:
661         case DHCP6_RENEW:
662         case DHCP6_REBIND:
663         case DHCP6_RELEASE:
664         case DHCP6_DECLINE:
665         case DHCP6_INFORMATION_REQUEST:
666         case DHCP6_RELAY_FORW:
667         case DHCP6_RELAY_REPL:
668                 return 0;
669
670         case DHCP6_ADVERTISE:
671         case DHCP6_REPLY:
672         case DHCP6_RECONFIGURE:
673                 break;
674
675         default:
676                 log_dhcp6_client(client, "unknown message type %d",
677                                  message->type);
678                 return 0;
679         }
680
681         if (client->transaction_id != (message->transaction_id &
682                                        htobe32(0x00ffffff)))
683                 return 0;
684
685         switch (client->state) {
686         case DHCP6_STATE_SOLICITATION:
687                 r = client_receive_advertise(client, message, len);
688
689                 if (r == DHCP6_STATE_REQUEST)
690                         client_start(client, r);
691
692                 break;
693
694         case DHCP6_STATE_REQUEST:
695                 r = client_receive_reply(client, message, len);
696                 if (r < 0)
697                         return 0;
698
699                 if (r == DHCP6_STATE_BOUND) {
700
701                         r = client_start(client, DHCP6_STATE_BOUND);
702                         if (r < 0) {
703                                 client_stop(client, r);
704                                 return 0;
705                         }
706
707                         client = client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
708                         if (!client)
709                                 return 0;
710                 }
711
712                 break;
713
714         case DHCP6_STATE_BOUND:
715
716                 break;
717
718         case DHCP6_STATE_STOPPED:
719         case DHCP6_STATE_RS:
720                 return 0;
721         }
722
723         if (r >= 0) {
724                 log_dhcp6_client(client, "Recv %s",
725                                  dhcp6_message_type_to_string(message->type));
726         }
727
728         return 0;
729 }
730
731 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
732 {
733         int r;
734         usec_t timeout, time_now;
735         char time_string[FORMAT_TIMESPAN_MAX];
736
737         assert_return(client, -EINVAL);
738         assert_return(client->event, -EINVAL);
739         assert_return(client->index > 0, -EINVAL);
740         assert_return(client->state != state, -EINVAL);
741
742         client->timeout_resend_expire =
743                 sd_event_source_unref(client->timeout_resend_expire);
744         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
745         client->retransmit_time = 0;
746         client->retransmit_count = 0;
747
748         switch (state) {
749         case DHCP6_STATE_STOPPED:
750         case DHCP6_STATE_RS:
751         case DHCP6_STATE_SOLICITATION:
752
753                 r = client_ensure_iaid(client);
754                 if (r < 0)
755                         return r;
756
757                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
758                 if (r < 0)
759                         return r;
760
761                 client->fd = r;
762
763                 r = sd_event_add_io(client->event, &client->receive_message,
764                                     client->fd, EPOLLIN, client_receive_message,
765                                     client);
766                 if (r < 0)
767                         return r;
768
769                 r = sd_event_source_set_priority(client->receive_message,
770                                                  client->event_priority);
771                 if (r < 0)
772                         return r;
773
774                 client->state = DHCP6_STATE_SOLICITATION;
775
776                 break;
777
778         case DHCP6_STATE_REQUEST:
779
780                 client->state = state;
781
782                 break;
783
784         case DHCP6_STATE_BOUND:
785
786                 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
787                 if (r < 0)
788                         return r;
789
790                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
791                     client->lease->ia.lifetime_t2 == 0xffffffff) {
792
793                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
794                                          be32toh(client->lease->ia.lifetime_t1),
795                                          be32toh(client->lease->ia.lifetime_t2));
796
797                         return 0;
798                 }
799
800                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
801
802                 log_dhcp6_client(client, "T1 expires in %s",
803                                  format_timespan(time_string,
804                                                  FORMAT_TIMESPAN_MAX,
805                                                  timeout, 0));
806
807                 r = sd_event_add_time(client->event,
808                                       &client->lease->ia.timeout_t1,
809                                       CLOCK_MONOTONIC, time_now + timeout,
810                                       10 * USEC_PER_SEC, client_timeout_t1,
811                                       client);
812                 if (r < 0)
813                         return r;
814
815                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
816                                                  client->event_priority);
817                 if (r < 0)
818                         return r;
819
820                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
821
822                 log_dhcp6_client(client, "T2 expires in %s",
823                                  format_timespan(time_string,
824                                                  FORMAT_TIMESPAN_MAX,
825                                                  timeout, 0));
826
827                 r = sd_event_add_time(client->event,
828                                       &client->lease->ia.timeout_t2,
829                                       CLOCK_MONOTONIC, time_now + timeout,
830                                       10 * USEC_PER_SEC, client_timeout_t2,
831                                       client);
832                 if (r < 0)
833                         return r;
834
835                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
836                                                  client->event_priority);
837                 if (r < 0)
838                         return r;
839
840                 return 0;
841         }
842
843         client->transaction_id = random_u32() & htobe32(0x00ffffff);
844
845         r = sd_event_add_time(client->event, &client->timeout_resend,
846                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
847                               client);
848         if (r < 0)
849                 return r;
850
851         r = sd_event_source_set_priority(client->timeout_resend,
852                                          client->event_priority);
853         if (r < 0)
854                 return r;
855
856         return 0;
857 }
858
859 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
860 {
861         client_stop(client, DHCP6_EVENT_STOP);
862
863         return 0;
864 }
865
866 int sd_dhcp6_client_start(sd_dhcp6_client *client)
867 {
868         int r = 0;
869
870         assert_return(client, -EINVAL);
871         assert_return(client->event, -EINVAL);
872         assert_return(client->index > 0, -EINVAL);
873
874         r = client_reset(client);
875         if (r < 0)
876                 return r;
877
878         return client_start(client, DHCP6_STATE_SOLICITATION);
879 }
880
881 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
882                                  int priority)
883 {
884         int r;
885
886         assert_return(client, -EINVAL);
887         assert_return(!client->event, -EBUSY);
888
889         if (event)
890                 client->event = sd_event_ref(event);
891         else {
892                 r = sd_event_default(&client->event);
893                 if (r < 0)
894                         return 0;
895         }
896
897         client->event_priority = priority;
898
899         return 0;
900 }
901
902 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
903         assert_return(client, -EINVAL);
904
905         client->event = sd_event_unref(client->event);
906
907         return 0;
908 }
909
910 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
911         if (!client)
912                 return NULL;
913
914         return client->event;
915 }
916
917 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
918         if (client)
919                 assert_se(REFCNT_INC(client->n_ref) >= 2);
920
921         return client;
922 }
923
924 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
925         if (client && REFCNT_DEC(client->n_ref) <= 0) {
926                 client_reset(client);
927
928                 sd_dhcp6_client_detach_event(client);
929
930                 free(client);
931
932                 return NULL;
933         }
934
935         return client;
936 }
937
938 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
939 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
940
941 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
942 {
943         _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
944         sd_id128_t machine_id;
945         int r;
946
947         assert_return(ret, -EINVAL);
948
949         client = new0(sd_dhcp6_client, 1);
950         if (!client)
951                 return -ENOMEM;
952
953         client->n_ref = REFCNT_INIT;
954
955         client->ia_na.type = DHCP6_OPTION_IA_NA;
956
957         client->index = -1;
958
959         client->fd = -1;
960
961         /* initialize DUID */
962         client->duid.type = htobe16(DHCP6_DUID_EN);
963         client->duid.pen = htobe32(SYSTEMD_PEN);
964
965         r = sd_id128_get_machine(&machine_id);
966         if (r < 0)
967                 return r;
968
969         /* a bit of snake-oil perhaps, but no need to expose the machine-id
970            directly */
971         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
972                   HASH_KEY.bytes);
973
974         *ret = client;
975         client = NULL;
976
977         return 0;
978 }