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