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