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