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