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