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