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