chiark / gitweb /
sd-dhcp6-client: initialize variable
[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 = 0;
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
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
420                 break;
421
422         case DHCP6_STATE_RENEW:
423                 init_retransmit_time = DHCP6_REN_TIMEOUT;
424                 max_retransmit_time = DHCP6_REN_MAX_RT;
425
426                 /* RFC 3315, section 18.1.3. says max retransmit duration will
427                    be the remaining time until T2. Instead of setting MRD,
428                    wait for T2 to trigger with the same end result */
429
430                 break;
431
432         case DHCP6_STATE_REBIND:
433                 init_retransmit_time = DHCP6_REB_TIMEOUT;
434                 max_retransmit_time = DHCP6_REB_MAX_RT;
435
436                 if (!client->timeout_resend_expire) {
437                         r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
438                                                          &expire);
439                         if (r < 0) {
440                                 client_stop(client, r);
441                                 return 0;
442                         }
443                         max_retransmit_duration = expire * USEC_PER_SEC;
444                 }
445
446                 break;
447
448         case DHCP6_STATE_STOPPED:
449         case DHCP6_STATE_RS:
450         case DHCP6_STATE_BOUND:
451                 return 0;
452         }
453
454         if (max_retransmit_count &&
455             client->retransmit_count >= max_retransmit_count) {
456                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
457                 return 0;
458         }
459
460         r = client_send_message(client);
461         if (r >= 0)
462                 client->retransmit_count++;
463
464
465         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
466         if (r < 0)
467                 goto error;
468
469         if (!client->retransmit_time) {
470                 client->retransmit_time =
471                         client_timeout_compute_random(init_retransmit_time);
472
473                 if (client->state == DHCP6_STATE_SOLICITATION)
474                         client->retransmit_time += init_retransmit_time / 10;
475
476         } else {
477                 if (max_retransmit_time &&
478                     client->retransmit_time > max_retransmit_time / 2)
479                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
480                 else
481                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
482         }
483
484         log_dhcp6_client(client, "Next retransmission in %s",
485                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
486                                          client->retransmit_time, 0));
487
488         r = sd_event_add_time(client->event, &client->timeout_resend,
489                               CLOCK_MONOTONIC,
490                               time_now + client->retransmit_time,
491                               10 * USEC_PER_MSEC, client_timeout_resend,
492                               client);
493         if (r < 0)
494                 goto error;
495
496         r = sd_event_source_set_priority(client->timeout_resend,
497                                          client->event_priority);
498         if (r < 0)
499                 goto error;
500
501         if (max_retransmit_duration && !client->timeout_resend_expire) {
502
503                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
504                                  max_retransmit_duration / USEC_PER_SEC);
505
506                 r = sd_event_add_time(client->event,
507                                       &client->timeout_resend_expire,
508                                       CLOCK_MONOTONIC,
509                                       time_now + max_retransmit_duration,
510                                       USEC_PER_SEC,
511                                       client_timeout_resend_expire, client);
512                 if (r < 0)
513                         goto error;
514
515                 r = sd_event_source_set_priority(client->timeout_resend_expire,
516                                                  client->event_priority);
517                 if (r < 0)
518                         goto error;
519         }
520
521 error:
522         if (r < 0)
523                 client_stop(client, r);
524
525         return 0;
526 }
527
528 static int client_ensure_iaid(sd_dhcp6_client *client) {
529         /* name is a pointer to memory in the udev_device struct, so must
530            have the same scope */
531         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
532         const char *name = NULL;
533         uint64_t id;
534
535         assert(client);
536
537         if (client->ia_na.id)
538                 return 0;
539
540         if (detect_container(NULL) <= 0) {
541                 /* not in a container, udev will be around */
542                 _cleanup_udev_unref_ struct udev *udev;
543                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
544
545                 udev = udev_new();
546                 if (!udev)
547                         return -ENOMEM;
548
549                 sprintf(ifindex_str, "n%d", client->index);
550                 device = udev_device_new_from_device_id(udev, ifindex_str);
551                 if (!device)
552                         return -errno;
553
554                 if (udev_device_get_is_initialized(device) <= 0)
555                         /* not yet ready */
556                         return -EBUSY;
557
558                 name = net_get_name(device);
559         }
560
561         if (name)
562                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
563         else
564                 /* fall back to mac address if no predictable name available */
565                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
566                           HASH_KEY.bytes);
567
568         /* fold into 32 bits */
569         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
570
571         return 0;
572 }
573
574 static int client_parse_message(sd_dhcp6_client *client,
575                                 DHCP6Message *message, size_t len,
576                                 sd_dhcp6_lease *lease) {
577         int r;
578         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
579         uint16_t optcode, status;
580         size_t optlen, id_len;
581         bool clientid = false;
582         be32_t iaid_lease;
583
584         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
585                                        &optval)) >= 0) {
586                 switch (optcode) {
587                 case DHCP6_OPTION_CLIENTID:
588                         if (clientid) {
589                                 log_dhcp6_client(client, "%s contains multiple clientids",
590                                                  dhcp6_message_type_to_string(message->type));
591                                 return -EINVAL;
592                         }
593
594                         if (optlen != sizeof(client->duid) ||
595                             memcmp(&client->duid, optval, optlen) != 0) {
596                                 log_dhcp6_client(client, "%s DUID does not match",
597                                                  dhcp6_message_type_to_string(message->type));
598
599                                 return -EINVAL;
600                         }
601                         clientid = true;
602
603                         break;
604
605                 case DHCP6_OPTION_SERVERID:
606                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
607                         if (r >= 0 && id) {
608                                 log_dhcp6_client(client, "%s contains multiple serverids",
609                                                  dhcp6_message_type_to_string(message->type));
610                                 return -EINVAL;
611                         }
612
613                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
614                         if (r < 0)
615                                 return r;
616
617                         break;
618
619                 case DHCP6_OPTION_PREFERENCE:
620                         if (optlen != 1)
621                                 return -EINVAL;
622
623                         r = dhcp6_lease_set_preference(lease, *optval);
624                         if (r < 0)
625                                 return r;
626
627                         break;
628
629                 case DHCP6_OPTION_STATUS_CODE:
630                         if (optlen < 2)
631                                 return -EINVAL;
632
633                         status = optval[0] << 8 | optval[1];
634                         if (status) {
635                                 log_dhcp6_client(client, "%s Status %s",
636                                                  dhcp6_message_type_to_string(message->type),
637                                                  dhcp6_message_status_to_string(status));
638                                 return -EINVAL;
639                         }
640
641                         break;
642
643                 case DHCP6_OPTION_IA_NA:
644                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
645                                                   &lease->ia);
646                         if (r < 0 && r != -ENOMSG)
647                                 return r;
648
649                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
650                         if (r < 0)
651                                 return r;
652
653                         if (client->ia_na.id != iaid_lease) {
654                                 log_dhcp6_client(client, "%s has wrong IAID",
655                                                  dhcp6_message_type_to_string(message->type));
656                                 return -EINVAL;
657                         }
658
659                         break;
660
661                 case DHCP6_OPTION_RAPID_COMMIT:
662                         r = dhcp6_lease_set_rapid_commit(lease);
663                         if (r < 0)
664                                 return r;
665
666                         break;
667                 }
668         }
669
670         if ((r < 0 && r != -ENOMSG) || !clientid) {
671                 log_dhcp6_client(client, "%s has incomplete options",
672                                  dhcp6_message_type_to_string(message->type));
673                 return -EINVAL;
674         }
675
676         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
677         if (r < 0)
678                 log_dhcp6_client(client, "%s has no server id",
679                                  dhcp6_message_type_to_string(message->type));
680
681         return r;
682 }
683
684 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
685                                 size_t len)
686 {
687         int r;
688         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
689         bool rapid_commit;
690
691         if (reply->type != DHCP6_REPLY)
692                 return 0;
693
694         r = dhcp6_lease_new(&lease);
695         if (r < 0)
696                 return -ENOMEM;
697
698         r = client_parse_message(client, reply, len, lease);
699         if (r < 0)
700                 return r;
701
702         if (client->state == DHCP6_STATE_SOLICITATION) {
703                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
704                 if (r < 0)
705                         return r;
706
707                 if (!rapid_commit)
708                         return 0;
709         }
710
711         dhcp6_lease_clear_timers(&client->lease->ia);
712
713         client->lease = sd_dhcp6_lease_unref(client->lease);
714         client->lease = lease;
715         lease = NULL;
716
717         return DHCP6_STATE_BOUND;
718 }
719
720 static int client_receive_advertise(sd_dhcp6_client *client,
721                                     DHCP6Message *advertise, size_t len) {
722         int r;
723         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
724         uint8_t pref_advertise = 0, pref_lease = 0;
725
726         if (advertise->type != DHCP6_ADVERTISE)
727                 return 0;
728
729         r = dhcp6_lease_new(&lease);
730         if (r < 0)
731                 return r;
732
733         r = client_parse_message(client, advertise, len, lease);
734         if (r < 0)
735                 return r;
736
737         r = dhcp6_lease_get_preference(lease, &pref_advertise);
738         if (r < 0)
739                 return r;
740
741         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
742         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
743                 sd_dhcp6_lease_unref(client->lease);
744                 client->lease = lease;
745                 lease = NULL;
746                 r = 0;
747         }
748
749         if (pref_advertise == 255 || client->retransmit_count > 1)
750                 r = DHCP6_STATE_REQUEST;
751
752         return r;
753 }
754
755 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
756                                   void *userdata) {
757         sd_dhcp6_client *client = userdata;
758         DHCP6_CLIENT_DONT_DESTROY(client);
759         _cleanup_free_ DHCP6Message *message;
760         int r, buflen, len;
761
762         assert(s);
763         assert(client);
764         assert(client->event);
765
766         r = ioctl(fd, FIONREAD, &buflen);
767         if (r < 0 || buflen <= 0)
768                 buflen = DHCP6_MIN_OPTIONS_SIZE;
769
770         message = malloc0(buflen);
771         if (!message)
772                 return -ENOMEM;
773
774         len = read(fd, message, buflen);
775         if ((size_t)len < sizeof(DHCP6Message)) {
776                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
777                 return 0;
778         }
779
780         switch(message->type) {
781         case DHCP6_SOLICIT:
782         case DHCP6_REQUEST:
783         case DHCP6_CONFIRM:
784         case DHCP6_RENEW:
785         case DHCP6_REBIND:
786         case DHCP6_RELEASE:
787         case DHCP6_DECLINE:
788         case DHCP6_INFORMATION_REQUEST:
789         case DHCP6_RELAY_FORW:
790         case DHCP6_RELAY_REPL:
791                 return 0;
792
793         case DHCP6_ADVERTISE:
794         case DHCP6_REPLY:
795         case DHCP6_RECONFIGURE:
796                 break;
797
798         default:
799                 log_dhcp6_client(client, "unknown message type %d",
800                                  message->type);
801                 return 0;
802         }
803
804         if (client->transaction_id != (message->transaction_id &
805                                        htobe32(0x00ffffff)))
806                 return 0;
807
808         switch (client->state) {
809         case DHCP6_STATE_SOLICITATION:
810                 r = client_receive_advertise(client, message, len);
811
812                 if (r == DHCP6_STATE_REQUEST) {
813                         client_start(client, r);
814
815                         break;
816                 }
817
818                 /* fall through for Soliciation Rapid Commit option check */
819         case DHCP6_STATE_REQUEST:
820         case DHCP6_STATE_RENEW:
821         case DHCP6_STATE_REBIND:
822
823                 r = client_receive_reply(client, message, len);
824                 if (r < 0)
825                         return 0;
826
827                 if (r == DHCP6_STATE_BOUND) {
828
829                         r = client_start(client, DHCP6_STATE_BOUND);
830                         if (r < 0) {
831                                 client_stop(client, r);
832                                 return 0;
833                         }
834
835                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
836                 }
837
838                 break;
839
840         case DHCP6_STATE_BOUND:
841
842                 break;
843
844         case DHCP6_STATE_STOPPED:
845         case DHCP6_STATE_RS:
846                 return 0;
847         }
848
849         if (r >= 0) {
850                 log_dhcp6_client(client, "Recv %s",
851                                  dhcp6_message_type_to_string(message->type));
852         }
853
854         return 0;
855 }
856
857 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
858 {
859         int r;
860         usec_t timeout, time_now;
861         char time_string[FORMAT_TIMESPAN_MAX];
862
863         assert_return(client, -EINVAL);
864         assert_return(client->event, -EINVAL);
865         assert_return(client->index > 0, -EINVAL);
866         assert_return(client->state != state, -EINVAL);
867
868         client->timeout_resend_expire =
869                 sd_event_source_unref(client->timeout_resend_expire);
870         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
871         client->retransmit_time = 0;
872         client->retransmit_count = 0;
873
874         switch (state) {
875         case DHCP6_STATE_STOPPED:
876         case DHCP6_STATE_RS:
877         case DHCP6_STATE_SOLICITATION:
878
879                 r = client_ensure_iaid(client);
880                 if (r < 0)
881                         return r;
882
883                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
884                 if (r < 0)
885                         return r;
886
887                 client->fd = r;
888
889                 r = sd_event_add_io(client->event, &client->receive_message,
890                                     client->fd, EPOLLIN, client_receive_message,
891                                     client);
892                 if (r < 0)
893                         return r;
894
895                 r = sd_event_source_set_priority(client->receive_message,
896                                                  client->event_priority);
897                 if (r < 0)
898                         return r;
899
900                 client->state = DHCP6_STATE_SOLICITATION;
901
902                 break;
903
904         case DHCP6_STATE_REQUEST:
905         case DHCP6_STATE_RENEW:
906         case DHCP6_STATE_REBIND:
907
908                 client->state = state;
909
910                 break;
911
912         case DHCP6_STATE_BOUND:
913
914                 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
915                 if (r < 0)
916                         return r;
917
918                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
919                     client->lease->ia.lifetime_t2 == 0xffffffff) {
920
921                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
922                                          be32toh(client->lease->ia.lifetime_t1),
923                                          be32toh(client->lease->ia.lifetime_t2));
924
925                         return 0;
926                 }
927
928                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
929
930                 log_dhcp6_client(client, "T1 expires in %s",
931                                  format_timespan(time_string,
932                                                  FORMAT_TIMESPAN_MAX,
933                                                  timeout, 0));
934
935                 r = sd_event_add_time(client->event,
936                                       &client->lease->ia.timeout_t1,
937                                       CLOCK_MONOTONIC, time_now + timeout,
938                                       10 * USEC_PER_SEC, client_timeout_t1,
939                                       client);
940                 if (r < 0)
941                         return r;
942
943                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
944                                                  client->event_priority);
945                 if (r < 0)
946                         return r;
947
948                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
949
950                 log_dhcp6_client(client, "T2 expires in %s",
951                                  format_timespan(time_string,
952                                                  FORMAT_TIMESPAN_MAX,
953                                                  timeout, 0));
954
955                 r = sd_event_add_time(client->event,
956                                       &client->lease->ia.timeout_t2,
957                                       CLOCK_MONOTONIC, time_now + timeout,
958                                       10 * USEC_PER_SEC, client_timeout_t2,
959                                       client);
960                 if (r < 0)
961                         return r;
962
963                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
964                                                  client->event_priority);
965                 if (r < 0)
966                         return r;
967
968                 client->state = state;
969
970                 return 0;
971         }
972
973         client->transaction_id = random_u32() & htobe32(0x00ffffff);
974
975         r = sd_event_add_time(client->event, &client->timeout_resend,
976                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
977                               client);
978         if (r < 0)
979                 return r;
980
981         r = sd_event_source_set_priority(client->timeout_resend,
982                                          client->event_priority);
983         if (r < 0)
984                 return r;
985
986         return 0;
987 }
988
989 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
990 {
991         client_stop(client, DHCP6_EVENT_STOP);
992
993         return 0;
994 }
995
996 int sd_dhcp6_client_start(sd_dhcp6_client *client)
997 {
998         int r = 0;
999
1000         assert_return(client, -EINVAL);
1001         assert_return(client->event, -EINVAL);
1002         assert_return(client->index > 0, -EINVAL);
1003
1004         r = client_reset(client);
1005         if (r < 0)
1006                 return r;
1007
1008         return client_start(client, DHCP6_STATE_SOLICITATION);
1009 }
1010
1011 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1012                                  int priority)
1013 {
1014         int r;
1015
1016         assert_return(client, -EINVAL);
1017         assert_return(!client->event, -EBUSY);
1018
1019         if (event)
1020                 client->event = sd_event_ref(event);
1021         else {
1022                 r = sd_event_default(&client->event);
1023                 if (r < 0)
1024                         return 0;
1025         }
1026
1027         client->event_priority = priority;
1028
1029         return 0;
1030 }
1031
1032 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1033         assert_return(client, -EINVAL);
1034
1035         client->event = sd_event_unref(client->event);
1036
1037         return 0;
1038 }
1039
1040 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1041         if (!client)
1042                 return NULL;
1043
1044         return client->event;
1045 }
1046
1047 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1048         if (client)
1049                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1050
1051         return client;
1052 }
1053
1054 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1055         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1056                 client_reset(client);
1057
1058                 sd_dhcp6_client_detach_event(client);
1059
1060                 free(client->req_opts);
1061                 free(client);
1062
1063                 return NULL;
1064         }
1065
1066         return client;
1067 }
1068
1069 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1070 {
1071         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1072         sd_id128_t machine_id;
1073         int r;
1074         size_t t;
1075
1076         assert_return(ret, -EINVAL);
1077
1078         client = new0(sd_dhcp6_client, 1);
1079         if (!client)
1080                 return -ENOMEM;
1081
1082         client->n_ref = REFCNT_INIT;
1083
1084         client->ia_na.type = DHCP6_OPTION_IA_NA;
1085
1086         client->index = -1;
1087
1088         client->fd = -1;
1089
1090         /* initialize DUID */
1091         client->duid.type = htobe16(DHCP6_DUID_EN);
1092         client->duid.pen = htobe32(SYSTEMD_PEN);
1093
1094         r = sd_id128_get_machine(&machine_id);
1095         if (r < 0)
1096                 return r;
1097
1098         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1099            directly */
1100         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1101                   HASH_KEY.bytes);
1102
1103         client->req_opts_len = ELEMENTSOF(default_req_opts);
1104
1105         client->req_opts = new0(be16_t, client->req_opts_len);
1106         if (!client->req_opts)
1107                 return -ENOMEM;
1108
1109         for (t = 0; t < client->req_opts_len; t++)
1110                 client->req_opts[t] = htobe16(default_req_opts[t]);
1111
1112         *ret = client;
1113         client = NULL;
1114
1115         return 0;
1116 }