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