chiark / gitweb /
c6c82eb349161ec4d856b0648c968045e73a343d
[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         if (max_retransmit_duration && !client->timeout_resend_expire) {
500
501                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
502                                  max_retransmit_duration / USEC_PER_SEC);
503
504                 r = sd_event_add_time(client->event,
505                                       &client->timeout_resend_expire,
506                                       clock_boottime_or_monotonic(),
507                                       time_now + max_retransmit_duration,
508                                       USEC_PER_SEC,
509                                       client_timeout_resend_expire, client);
510                 if (r < 0)
511                         goto error;
512
513                 r = sd_event_source_set_priority(client->timeout_resend_expire,
514                                                  client->event_priority);
515                 if (r < 0)
516                         goto error;
517         }
518
519 error:
520         if (r < 0)
521                 client_stop(client, r);
522
523         return 0;
524 }
525
526 static int client_ensure_iaid(sd_dhcp6_client *client) {
527         /* name is a pointer to memory in the udev_device struct, so must
528            have the same scope */
529         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
530         const char *name = NULL;
531         uint64_t id;
532
533         assert(client);
534
535         if (client->ia_na.id)
536                 return 0;
537
538         if (detect_container(NULL) <= 0) {
539                 /* not in a container, udev will be around */
540                 _cleanup_udev_unref_ struct udev *udev;
541                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
542
543                 udev = udev_new();
544                 if (!udev)
545                         return -ENOMEM;
546
547                 sprintf(ifindex_str, "n%d", client->index);
548                 device = udev_device_new_from_device_id(udev, ifindex_str);
549                 if (!device)
550                         return -errno;
551
552                 if (udev_device_get_is_initialized(device) <= 0)
553                         /* not yet ready */
554                         return -EBUSY;
555
556                 name = net_get_name(device);
557         }
558
559         if (name)
560                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
561         else
562                 /* fall back to mac address if no predictable name available */
563                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
564                           HASH_KEY.bytes);
565
566         /* fold into 32 bits */
567         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
568
569         return 0;
570 }
571
572 static int client_parse_message(sd_dhcp6_client *client,
573                                 DHCP6Message *message, size_t len,
574                                 sd_dhcp6_lease *lease) {
575         int r;
576         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
577         uint16_t optcode, status;
578         size_t optlen, id_len;
579         bool clientid = false;
580         be32_t iaid_lease;
581
582         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
583                                        &optval)) >= 0) {
584                 switch (optcode) {
585                 case DHCP6_OPTION_CLIENTID:
586                         if (clientid) {
587                                 log_dhcp6_client(client, "%s contains multiple clientids",
588                                                  dhcp6_message_type_to_string(message->type));
589                                 return -EINVAL;
590                         }
591
592                         if (optlen != sizeof(client->duid) ||
593                             memcmp(&client->duid, optval, optlen) != 0) {
594                                 log_dhcp6_client(client, "%s DUID does not match",
595                                                  dhcp6_message_type_to_string(message->type));
596
597                                 return -EINVAL;
598                         }
599                         clientid = true;
600
601                         break;
602
603                 case DHCP6_OPTION_SERVERID:
604                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
605                         if (r >= 0 && id) {
606                                 log_dhcp6_client(client, "%s contains multiple serverids",
607                                                  dhcp6_message_type_to_string(message->type));
608                                 return -EINVAL;
609                         }
610
611                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
612                         if (r < 0)
613                                 return r;
614
615                         break;
616
617                 case DHCP6_OPTION_PREFERENCE:
618                         if (optlen != 1)
619                                 return -EINVAL;
620
621                         r = dhcp6_lease_set_preference(lease, *optval);
622                         if (r < 0)
623                                 return r;
624
625                         break;
626
627                 case DHCP6_OPTION_STATUS_CODE:
628                         if (optlen < 2)
629                                 return -EINVAL;
630
631                         status = optval[0] << 8 | optval[1];
632                         if (status) {
633                                 log_dhcp6_client(client, "%s Status %s",
634                                                  dhcp6_message_type_to_string(message->type),
635                                                  dhcp6_message_status_to_string(status));
636                                 return -EINVAL;
637                         }
638
639                         break;
640
641                 case DHCP6_OPTION_IA_NA:
642                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
643                                                   &lease->ia);
644                         if (r < 0 && r != -ENOMSG)
645                                 return r;
646
647                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
648                         if (r < 0)
649                                 return r;
650
651                         if (client->ia_na.id != iaid_lease) {
652                                 log_dhcp6_client(client, "%s has wrong IAID",
653                                                  dhcp6_message_type_to_string(message->type));
654                                 return -EINVAL;
655                         }
656
657                         break;
658
659                 case DHCP6_OPTION_RAPID_COMMIT:
660                         r = dhcp6_lease_set_rapid_commit(lease);
661                         if (r < 0)
662                                 return r;
663
664                         break;
665                 }
666         }
667
668         if ((r < 0 && r != -ENOMSG) || !clientid) {
669                 log_dhcp6_client(client, "%s has incomplete options",
670                                  dhcp6_message_type_to_string(message->type));
671                 return -EINVAL;
672         }
673
674         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
675         if (r < 0)
676                 log_dhcp6_client(client, "%s has no server id",
677                                  dhcp6_message_type_to_string(message->type));
678
679         return r;
680 }
681
682 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
683                                 size_t len)
684 {
685         int r;
686         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
687         bool rapid_commit;
688
689         if (reply->type != DHCP6_REPLY)
690                 return 0;
691
692         r = dhcp6_lease_new(&lease);
693         if (r < 0)
694                 return -ENOMEM;
695
696         r = client_parse_message(client, reply, len, lease);
697         if (r < 0)
698                 return r;
699
700         if (client->state == DHCP6_STATE_SOLICITATION) {
701                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
702                 if (r < 0)
703                         return r;
704
705                 if (!rapid_commit)
706                         return 0;
707         }
708
709         if (client->lease)
710             dhcp6_lease_clear_timers(&client->lease->ia);
711
712         client->lease = sd_dhcp6_lease_unref(client->lease);
713         client->lease = lease;
714         lease = NULL;
715
716         return DHCP6_STATE_BOUND;
717 }
718
719 static int client_receive_advertise(sd_dhcp6_client *client,
720                                     DHCP6Message *advertise, size_t len) {
721         int r;
722         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
723         uint8_t pref_advertise = 0, pref_lease = 0;
724
725         if (advertise->type != DHCP6_ADVERTISE)
726                 return 0;
727
728         r = dhcp6_lease_new(&lease);
729         if (r < 0)
730                 return r;
731
732         r = client_parse_message(client, advertise, len, lease);
733         if (r < 0)
734                 return r;
735
736         r = dhcp6_lease_get_preference(lease, &pref_advertise);
737         if (r < 0)
738                 return r;
739
740         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
741         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
742                 sd_dhcp6_lease_unref(client->lease);
743                 client->lease = lease;
744                 lease = NULL;
745                 r = 0;
746         }
747
748         if (pref_advertise == 255 || client->retransmit_count > 1)
749                 r = DHCP6_STATE_REQUEST;
750
751         return r;
752 }
753
754 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
755                                   void *userdata) {
756         sd_dhcp6_client *client = userdata;
757         DHCP6_CLIENT_DONT_DESTROY(client);
758         _cleanup_free_ DHCP6Message *message;
759         int r, buflen, len;
760
761         assert(s);
762         assert(client);
763         assert(client->event);
764
765         r = ioctl(fd, FIONREAD, &buflen);
766         if (r < 0 || buflen <= 0)
767                 buflen = DHCP6_MIN_OPTIONS_SIZE;
768
769         message = malloc0(buflen);
770         if (!message)
771                 return -ENOMEM;
772
773         len = read(fd, message, buflen);
774         if ((size_t)len < sizeof(DHCP6Message)) {
775                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
776                 return 0;
777         }
778
779         switch(message->type) {
780         case DHCP6_SOLICIT:
781         case DHCP6_REQUEST:
782         case DHCP6_CONFIRM:
783         case DHCP6_RENEW:
784         case DHCP6_REBIND:
785         case DHCP6_RELEASE:
786         case DHCP6_DECLINE:
787         case DHCP6_INFORMATION_REQUEST:
788         case DHCP6_RELAY_FORW:
789         case DHCP6_RELAY_REPL:
790                 return 0;
791
792         case DHCP6_ADVERTISE:
793         case DHCP6_REPLY:
794         case DHCP6_RECONFIGURE:
795                 break;
796
797         default:
798                 log_dhcp6_client(client, "unknown message type %d",
799                                  message->type);
800                 return 0;
801         }
802
803         if (client->transaction_id != (message->transaction_id &
804                                        htobe32(0x00ffffff)))
805                 return 0;
806
807         switch (client->state) {
808         case DHCP6_STATE_SOLICITATION:
809                 r = client_receive_advertise(client, message, len);
810
811                 if (r == DHCP6_STATE_REQUEST) {
812                         client_start(client, r);
813
814                         break;
815                 }
816
817                 /* fall through for Soliciation Rapid Commit option check */
818         case DHCP6_STATE_REQUEST:
819         case DHCP6_STATE_RENEW:
820         case DHCP6_STATE_REBIND:
821
822                 r = client_receive_reply(client, message, len);
823                 if (r < 0)
824                         return 0;
825
826                 if (r == DHCP6_STATE_BOUND) {
827
828                         r = client_start(client, DHCP6_STATE_BOUND);
829                         if (r < 0) {
830                                 client_stop(client, r);
831                                 return 0;
832                         }
833
834                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
835                 }
836
837                 break;
838
839         case DHCP6_STATE_BOUND:
840
841                 break;
842
843         case DHCP6_STATE_STOPPED:
844                 return 0;
845         }
846
847         if (r >= 0) {
848                 log_dhcp6_client(client, "Recv %s",
849                                  dhcp6_message_type_to_string(message->type));
850         }
851
852         return 0;
853 }
854
855 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
856 {
857         int r;
858         usec_t timeout, time_now;
859         char time_string[FORMAT_TIMESPAN_MAX];
860
861         assert_return(client, -EINVAL);
862         assert_return(client->event, -EINVAL);
863         assert_return(client->index > 0, -EINVAL);
864         assert_return(client->state != state, -EINVAL);
865
866         client->timeout_resend_expire =
867                 sd_event_source_unref(client->timeout_resend_expire);
868         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
869         client->retransmit_time = 0;
870         client->retransmit_count = 0;
871
872         switch (state) {
873         case DHCP6_STATE_STOPPED:
874         case DHCP6_STATE_SOLICITATION:
875
876                 r = client_ensure_iaid(client);
877                 if (r < 0)
878                         return r;
879
880                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
881                 if (r < 0)
882                         return r;
883
884                 client->fd = r;
885
886                 r = sd_event_add_io(client->event, &client->receive_message,
887                                     client->fd, EPOLLIN, client_receive_message,
888                                     client);
889                 if (r < 0)
890                         return r;
891
892                 r = sd_event_source_set_priority(client->receive_message,
893                                                  client->event_priority);
894                 if (r < 0)
895                         return r;
896
897                 client->state = DHCP6_STATE_SOLICITATION;
898
899                 break;
900
901         case DHCP6_STATE_REQUEST:
902         case DHCP6_STATE_RENEW:
903         case DHCP6_STATE_REBIND:
904
905                 client->state = state;
906
907                 break;
908
909         case DHCP6_STATE_BOUND:
910
911                 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
912                 if (r < 0)
913                         return r;
914
915                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
916                     client->lease->ia.lifetime_t2 == 0xffffffff) {
917
918                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
919                                          be32toh(client->lease->ia.lifetime_t1),
920                                          be32toh(client->lease->ia.lifetime_t2));
921
922                         return 0;
923                 }
924
925                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
926
927                 log_dhcp6_client(client, "T1 expires in %s",
928                                  format_timespan(time_string,
929                                                  FORMAT_TIMESPAN_MAX,
930                                                  timeout, 0));
931
932                 r = sd_event_add_time(client->event,
933                                       &client->lease->ia.timeout_t1,
934                                       clock_boottime_or_monotonic(), time_now + timeout,
935                                       10 * USEC_PER_SEC, client_timeout_t1,
936                                       client);
937                 if (r < 0)
938                         return r;
939
940                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
941                                                  client->event_priority);
942                 if (r < 0)
943                         return r;
944
945                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
946
947                 log_dhcp6_client(client, "T2 expires in %s",
948                                  format_timespan(time_string,
949                                                  FORMAT_TIMESPAN_MAX,
950                                                  timeout, 0));
951
952                 r = sd_event_add_time(client->event,
953                                       &client->lease->ia.timeout_t2,
954                                       clock_boottime_or_monotonic(), time_now + timeout,
955                                       10 * USEC_PER_SEC, client_timeout_t2,
956                                       client);
957                 if (r < 0)
958                         return r;
959
960                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
961                                                  client->event_priority);
962                 if (r < 0)
963                         return r;
964
965                 client->state = state;
966
967                 return 0;
968         }
969
970         client->transaction_id = random_u32() & htobe32(0x00ffffff);
971
972         r = sd_event_add_time(client->event, &client->timeout_resend,
973                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
974                               client);
975         if (r < 0)
976                 return r;
977
978         r = sd_event_source_set_priority(client->timeout_resend,
979                                          client->event_priority);
980         if (r < 0)
981                 return r;
982
983         return 0;
984 }
985
986 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
987 {
988         client_stop(client, DHCP6_EVENT_STOP);
989
990         return 0;
991 }
992
993 int sd_dhcp6_client_start(sd_dhcp6_client *client)
994 {
995         int r = 0;
996
997         assert_return(client, -EINVAL);
998         assert_return(client->event, -EINVAL);
999         assert_return(client->index > 0, -EINVAL);
1000
1001         r = client_reset(client);
1002         if (r < 0)
1003                 return r;
1004
1005         return client_start(client, DHCP6_STATE_SOLICITATION);
1006 }
1007
1008 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1009                                  int priority)
1010 {
1011         int r;
1012
1013         assert_return(client, -EINVAL);
1014         assert_return(!client->event, -EBUSY);
1015
1016         if (event)
1017                 client->event = sd_event_ref(event);
1018         else {
1019                 r = sd_event_default(&client->event);
1020                 if (r < 0)
1021                         return 0;
1022         }
1023
1024         client->event_priority = priority;
1025
1026         return 0;
1027 }
1028
1029 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1030         assert_return(client, -EINVAL);
1031
1032         client->event = sd_event_unref(client->event);
1033
1034         return 0;
1035 }
1036
1037 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1038         if (!client)
1039                 return NULL;
1040
1041         return client->event;
1042 }
1043
1044 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1045         if (client)
1046                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1047
1048         return client;
1049 }
1050
1051 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1052         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1053                 client_reset(client);
1054
1055                 sd_dhcp6_client_detach_event(client);
1056
1057                 free(client->req_opts);
1058                 free(client);
1059
1060                 return NULL;
1061         }
1062
1063         return client;
1064 }
1065
1066 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1067 {
1068         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1069         sd_id128_t machine_id;
1070         int r;
1071         size_t t;
1072
1073         assert_return(ret, -EINVAL);
1074
1075         client = new0(sd_dhcp6_client, 1);
1076         if (!client)
1077                 return -ENOMEM;
1078
1079         client->n_ref = REFCNT_INIT;
1080
1081         client->ia_na.type = DHCP6_OPTION_IA_NA;
1082
1083         client->index = -1;
1084
1085         client->fd = -1;
1086
1087         /* initialize DUID */
1088         client->duid.type = htobe16(DHCP6_DUID_EN);
1089         client->duid.pen = htobe32(SYSTEMD_PEN);
1090
1091         r = sd_id128_get_machine(&machine_id);
1092         if (r < 0)
1093                 return r;
1094
1095         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1096            directly */
1097         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1098                   HASH_KEY.bytes);
1099
1100         client->req_opts_len = ELEMENTSOF(default_req_opts);
1101
1102         client->req_opts = new0(be16_t, client->req_opts_len);
1103         if (!client->req_opts)
1104                 return -ENOMEM;
1105
1106         for (t = 0; t < client->req_opts_len; t++)
1107                 client->req_opts[t] = htobe16(default_req_opts[t]);
1108
1109         *ret = client;
1110         client = NULL;
1111
1112         return 0;
1113 }