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