chiark / gitweb /
dbec1a2a8b43903aaa1c8730345b98cefbde1102
[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_name(client->timeout_resend,
594                                      "dhcp6-resend-timer");
595         if (r < 0)
596                 goto error;
597
598         if (max_retransmit_duration && !client->timeout_resend_expire) {
599
600                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
601                                  max_retransmit_duration / USEC_PER_SEC);
602
603                 r = sd_event_add_time(client->event,
604                                       &client->timeout_resend_expire,
605                                       clock_boottime_or_monotonic(),
606                                       time_now + max_retransmit_duration,
607                                       USEC_PER_SEC,
608                                       client_timeout_resend_expire, client);
609                 if (r < 0)
610                         goto error;
611
612                 r = sd_event_source_set_priority(client->timeout_resend_expire,
613                                                  client->event_priority);
614                 if (r < 0)
615                         goto error;
616
617                 r = sd_event_source_set_name(client->timeout_resend_expire,
618                                              "dhcp6-resend-expire-timer");
619                 if (r < 0)
620                         goto error;
621         }
622
623 error:
624         if (r < 0)
625                 client_stop(client, r);
626
627         return 0;
628 }
629
630 static int client_ensure_iaid(sd_dhcp6_client *client) {
631         /* name is a pointer to memory in the udev_device struct, so must
632            have the same scope */
633         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
634         const char *name = NULL;
635         uint64_t id;
636
637         assert(client);
638
639         if (client->ia_na.id)
640                 return 0;
641
642         if (detect_container(NULL) <= 0) {
643                 /* not in a container, udev will be around */
644                 _cleanup_udev_unref_ struct udev *udev;
645                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
646
647                 udev = udev_new();
648                 if (!udev)
649                         return -ENOMEM;
650
651                 sprintf(ifindex_str, "n%d", client->index);
652                 device = udev_device_new_from_device_id(udev, ifindex_str);
653                 if (!device)
654                         return -errno;
655
656                 if (udev_device_get_is_initialized(device) <= 0)
657                         /* not yet ready */
658                         return -EBUSY;
659
660                 name = net_get_name(device);
661         }
662
663         if (name)
664                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
665         else
666                 /* fall back to mac address if no predictable name available */
667                 siphash24((uint8_t*)&id, &client->mac_addr,
668                           client->mac_addr_len, HASH_KEY.bytes);
669
670         /* fold into 32 bits */
671         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
672
673         return 0;
674 }
675
676 static int client_parse_message(sd_dhcp6_client *client,
677                                 DHCP6Message *message, size_t len,
678                                 sd_dhcp6_lease *lease) {
679         int r;
680         uint8_t *optval, *option, *id = NULL;
681         uint16_t optcode, status;
682         size_t optlen, id_len;
683         bool clientid = false;
684         be32_t iaid_lease;
685
686         option = (uint8_t *)message + sizeof(DHCP6Message);
687         len -= sizeof(DHCP6Message);
688
689         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
690                                        &optval)) >= 0) {
691                 switch (optcode) {
692                 case DHCP6_OPTION_CLIENTID:
693                         if (clientid) {
694                                 log_dhcp6_client(client, "%s contains multiple clientids",
695                                                  dhcp6_message_type_to_string(message->type));
696                                 return -EINVAL;
697                         }
698
699                         if (optlen != client->duid_len ||
700                             memcmp(&client->duid, optval, optlen) != 0) {
701                                 log_dhcp6_client(client, "%s DUID does not match",
702                                                  dhcp6_message_type_to_string(message->type));
703
704                                 return -EINVAL;
705                         }
706                         clientid = true;
707
708                         break;
709
710                 case DHCP6_OPTION_SERVERID:
711                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
712                         if (r >= 0 && id) {
713                                 log_dhcp6_client(client, "%s contains multiple serverids",
714                                                  dhcp6_message_type_to_string(message->type));
715                                 return -EINVAL;
716                         }
717
718                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
719                         if (r < 0)
720                                 return r;
721
722                         break;
723
724                 case DHCP6_OPTION_PREFERENCE:
725                         if (optlen != 1)
726                                 return -EINVAL;
727
728                         r = dhcp6_lease_set_preference(lease, *optval);
729                         if (r < 0)
730                                 return r;
731
732                         break;
733
734                 case DHCP6_OPTION_STATUS_CODE:
735                         if (optlen < 2)
736                                 return -EINVAL;
737
738                         status = optval[0] << 8 | optval[1];
739                         if (status) {
740                                 log_dhcp6_client(client, "%s Status %s",
741                                                  dhcp6_message_type_to_string(message->type),
742                                                  dhcp6_message_status_to_string(status));
743                                 return -EINVAL;
744                         }
745
746                         break;
747
748                 case DHCP6_OPTION_IA_NA:
749                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
750                                                   &lease->ia);
751                         if (r < 0 && r != -ENOMSG)
752                                 return r;
753
754                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
755                         if (r < 0)
756                                 return r;
757
758                         if (client->ia_na.id != iaid_lease) {
759                                 log_dhcp6_client(client, "%s has wrong IAID",
760                                                  dhcp6_message_type_to_string(message->type));
761                                 return -EINVAL;
762                         }
763
764                         break;
765
766                 case DHCP6_OPTION_RAPID_COMMIT:
767                         r = dhcp6_lease_set_rapid_commit(lease);
768                         if (r < 0)
769                                 return r;
770
771                         break;
772                 }
773         }
774
775         if ((r < 0 && r != -ENOMSG) || !clientid) {
776                 log_dhcp6_client(client, "%s has incomplete options",
777                                  dhcp6_message_type_to_string(message->type));
778                 return -EINVAL;
779         }
780
781         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
782         if (r < 0)
783                 log_dhcp6_client(client, "%s has no server id",
784                                  dhcp6_message_type_to_string(message->type));
785
786         return r;
787 }
788
789 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
790                                 size_t len)
791 {
792         int r;
793         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
794         bool rapid_commit;
795
796         if (reply->type != DHCP6_REPLY)
797                 return 0;
798
799         r = dhcp6_lease_new(&lease);
800         if (r < 0)
801                 return -ENOMEM;
802
803         r = client_parse_message(client, reply, len, lease);
804         if (r < 0)
805                 return r;
806
807         if (client->state == DHCP6_STATE_SOLICITATION) {
808                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
809                 if (r < 0)
810                         return r;
811
812                 if (!rapid_commit)
813                         return 0;
814         }
815
816         if (client->lease)
817                 dhcp6_lease_clear_timers(&client->lease->ia);
818
819         client->lease = sd_dhcp6_lease_unref(client->lease);
820         client->lease = lease;
821         lease = NULL;
822
823         return DHCP6_STATE_BOUND;
824 }
825
826 static int client_receive_advertise(sd_dhcp6_client *client,
827                                     DHCP6Message *advertise, size_t len) {
828         int r;
829         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
830         uint8_t pref_advertise = 0, pref_lease = 0;
831
832         if (advertise->type != DHCP6_ADVERTISE)
833                 return 0;
834
835         r = dhcp6_lease_new(&lease);
836         if (r < 0)
837                 return r;
838
839         r = client_parse_message(client, advertise, len, lease);
840         if (r < 0)
841                 return r;
842
843         r = dhcp6_lease_get_preference(lease, &pref_advertise);
844         if (r < 0)
845                 return r;
846
847         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
848         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
849                 sd_dhcp6_lease_unref(client->lease);
850                 client->lease = lease;
851                 lease = NULL;
852                 r = 0;
853         }
854
855         if (pref_advertise == 255 || client->retransmit_count > 1)
856                 r = DHCP6_STATE_REQUEST;
857
858         return r;
859 }
860
861 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
862                                   void *userdata) {
863         sd_dhcp6_client *client = userdata;
864         DHCP6_CLIENT_DONT_DESTROY(client);
865         _cleanup_free_ DHCP6Message *message;
866         int r, buflen, len;
867
868         assert(s);
869         assert(client);
870         assert(client->event);
871
872         r = ioctl(fd, FIONREAD, &buflen);
873         if (r < 0 || buflen <= 0)
874                 buflen = DHCP6_MIN_OPTIONS_SIZE;
875
876         message = malloc0(buflen);
877         if (!message)
878                 return -ENOMEM;
879
880         len = read(fd, message, buflen);
881         if ((size_t)len < sizeof(DHCP6Message)) {
882                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
883                 return 0;
884         }
885
886         switch(message->type) {
887         case DHCP6_SOLICIT:
888         case DHCP6_REQUEST:
889         case DHCP6_CONFIRM:
890         case DHCP6_RENEW:
891         case DHCP6_REBIND:
892         case DHCP6_RELEASE:
893         case DHCP6_DECLINE:
894         case DHCP6_INFORMATION_REQUEST:
895         case DHCP6_RELAY_FORW:
896         case DHCP6_RELAY_REPL:
897                 return 0;
898
899         case DHCP6_ADVERTISE:
900         case DHCP6_REPLY:
901         case DHCP6_RECONFIGURE:
902                 break;
903
904         default:
905                 log_dhcp6_client(client, "unknown message type %d",
906                                  message->type);
907                 return 0;
908         }
909
910         if (client->transaction_id != (message->transaction_id &
911                                        htobe32(0x00ffffff)))
912                 return 0;
913
914         switch (client->state) {
915         case DHCP6_STATE_SOLICITATION:
916                 r = client_receive_advertise(client, message, len);
917
918                 if (r == DHCP6_STATE_REQUEST) {
919                         client_start(client, r);
920
921                         break;
922                 }
923
924                 /* fall through for Soliciation Rapid Commit option check */
925         case DHCP6_STATE_REQUEST:
926         case DHCP6_STATE_RENEW:
927         case DHCP6_STATE_REBIND:
928
929                 r = client_receive_reply(client, message, len);
930                 if (r < 0)
931                         return 0;
932
933                 if (r == DHCP6_STATE_BOUND) {
934
935                         r = client_start(client, DHCP6_STATE_BOUND);
936                         if (r < 0) {
937                                 client_stop(client, r);
938                                 return 0;
939                         }
940
941                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
942                 }
943
944                 break;
945
946         case DHCP6_STATE_BOUND:
947
948                 break;
949
950         case DHCP6_STATE_STOPPED:
951                 return 0;
952         }
953
954         if (r >= 0) {
955                 log_dhcp6_client(client, "Recv %s",
956                                  dhcp6_message_type_to_string(message->type));
957         }
958
959         return 0;
960 }
961
962 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
963 {
964         int r;
965         usec_t timeout, time_now;
966         char time_string[FORMAT_TIMESPAN_MAX];
967
968         assert_return(client, -EINVAL);
969         assert_return(client->event, -EINVAL);
970         assert_return(client->index > 0, -EINVAL);
971         assert_return(client->state != state, -EINVAL);
972
973         client->timeout_resend_expire =
974                 sd_event_source_unref(client->timeout_resend_expire);
975         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
976         client->retransmit_time = 0;
977         client->retransmit_count = 0;
978
979         if (client->state == DHCP6_STATE_STOPPED) {
980                 time_now = now(clock_boottime_or_monotonic());
981         } else {
982                 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
983                                  &time_now);
984                 if (r < 0)
985                         return r;
986         }
987
988         switch (state) {
989         case DHCP6_STATE_STOPPED:
990         case DHCP6_STATE_SOLICITATION:
991
992                 r = client_ensure_iaid(client);
993                 if (r < 0)
994                         return r;
995
996                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
997                 if (r < 0)
998                         return r;
999
1000                 client->fd = r;
1001
1002                 r = sd_event_add_io(client->event, &client->receive_message,
1003                                     client->fd, EPOLLIN, client_receive_message,
1004                                     client);
1005                 if (r < 0)
1006                         return r;
1007
1008                 r = sd_event_source_set_priority(client->receive_message,
1009                                                  client->event_priority);
1010                 if (r < 0)
1011                         return r;
1012
1013                 r = sd_event_source_set_name(client->receive_message,
1014                                              "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_name(client->lease->ia.timeout_t1,
1063                                              "dhcp6-t1-timeout");
1064                 if (r < 0)
1065                         return r;
1066
1067                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1068
1069                 log_dhcp6_client(client, "T2 expires in %s",
1070                                  format_timespan(time_string,
1071                                                  FORMAT_TIMESPAN_MAX,
1072                                                  timeout, 0));
1073
1074                 r = sd_event_add_time(client->event,
1075                                       &client->lease->ia.timeout_t2,
1076                                       clock_boottime_or_monotonic(), time_now + timeout,
1077                                       10 * USEC_PER_SEC, client_timeout_t2,
1078                                       client);
1079                 if (r < 0)
1080                         return r;
1081
1082                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1083                                                  client->event_priority);
1084                 if (r < 0)
1085                         return r;
1086
1087                 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
1088                                              "dhcp6-t2-timeout");
1089                 if (r < 0)
1090                         return r;
1091
1092                 client->state = state;
1093
1094                 return 0;
1095         }
1096
1097         client->transaction_id = random_u32() & htobe32(0x00ffffff);
1098         client->transaction_start = time_now;
1099
1100         r = sd_event_add_time(client->event, &client->timeout_resend,
1101                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1102                               client);
1103         if (r < 0)
1104                 return r;
1105
1106         r = sd_event_source_set_priority(client->timeout_resend,
1107                                          client->event_priority);
1108         if (r < 0)
1109                 return r;
1110
1111         r = sd_event_source_set_name(client->timeout_resend,
1112                                      "dhcp6-resend-timeout");
1113         if (r < 0)
1114                 return r;
1115
1116         return 0;
1117 }
1118
1119 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1120 {
1121         client_stop(client, DHCP6_EVENT_STOP);
1122
1123         return 0;
1124 }
1125
1126 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1127 {
1128         int r = 0;
1129
1130         assert_return(client, -EINVAL);
1131         assert_return(client->event, -EINVAL);
1132         assert_return(client->index > 0, -EINVAL);
1133
1134         r = client_reset(client);
1135         if (r < 0)
1136                 return r;
1137
1138         return client_start(client, DHCP6_STATE_SOLICITATION);
1139 }
1140
1141 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1142                                  int priority)
1143 {
1144         int r;
1145
1146         assert_return(client, -EINVAL);
1147         assert_return(!client->event, -EBUSY);
1148
1149         if (event)
1150                 client->event = sd_event_ref(event);
1151         else {
1152                 r = sd_event_default(&client->event);
1153                 if (r < 0)
1154                         return 0;
1155         }
1156
1157         client->event_priority = priority;
1158
1159         return 0;
1160 }
1161
1162 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1163         assert_return(client, -EINVAL);
1164
1165         client->event = sd_event_unref(client->event);
1166
1167         return 0;
1168 }
1169
1170 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1171         if (!client)
1172                 return NULL;
1173
1174         return client->event;
1175 }
1176
1177 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1178         if (client)
1179                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1180
1181         return client;
1182 }
1183
1184 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1185         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1186                 client_reset(client);
1187
1188                 sd_dhcp6_client_detach_event(client);
1189
1190                 free(client->req_opts);
1191                 free(client);
1192
1193                 return NULL;
1194         }
1195
1196         return client;
1197 }
1198
1199 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1200 {
1201         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1202         sd_id128_t machine_id;
1203         int r;
1204         size_t t;
1205
1206         assert_return(ret, -EINVAL);
1207
1208         client = new0(sd_dhcp6_client, 1);
1209         if (!client)
1210                 return -ENOMEM;
1211
1212         client->n_ref = REFCNT_INIT;
1213
1214         client->ia_na.type = DHCP6_OPTION_IA_NA;
1215
1216         client->index = -1;
1217
1218         client->fd = -1;
1219
1220         /* initialize DUID */
1221         client->duid.en.type = htobe16(DHCP6_DUID_EN);
1222         client->duid.en.pen = htobe32(SYSTEMD_PEN);
1223         client->duid_len = sizeof(client->duid.en);
1224
1225         r = sd_id128_get_machine(&machine_id);
1226         if (r < 0)
1227                 return r;
1228
1229         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1230            directly */
1231         siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1232
1233         client->req_opts_len = ELEMENTSOF(default_req_opts);
1234
1235         client->req_opts = new0(be16_t, client->req_opts_len);
1236         if (!client->req_opts)
1237                 return -ENOMEM;
1238
1239         for (t = 0; t < client->req_opts_len; t++)
1240                 client->req_opts[t] = htobe16(default_req_opts[t]);
1241
1242         *ret = client;
1243         client = NULL;
1244
1245         return 0;
1246 }