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