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