chiark / gitweb /
resolved: make TXT RR generation and parsing more in-line with RFC 6763, section 6.1
[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 < 0 && r != -ENOMSG) || !clientid) {
774                 log_dhcp6_client(client, "%s has incomplete options",
775                                  dhcp6_message_type_to_string(message->type));
776                 return -EINVAL;
777         }
778
779         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
780         if (r < 0)
781                 log_dhcp6_client(client, "%s has no server id",
782                                  dhcp6_message_type_to_string(message->type));
783
784         return r;
785 }
786
787 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
788                                 size_t len)
789 {
790         int r;
791         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
792         bool rapid_commit;
793
794         if (reply->type != DHCP6_REPLY)
795                 return 0;
796
797         r = dhcp6_lease_new(&lease);
798         if (r < 0)
799                 return -ENOMEM;
800
801         r = client_parse_message(client, reply, len, lease);
802         if (r < 0)
803                 return r;
804
805         if (client->state == DHCP6_STATE_SOLICITATION) {
806                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
807                 if (r < 0)
808                         return r;
809
810                 if (!rapid_commit)
811                         return 0;
812         }
813
814         if (client->lease)
815                 dhcp6_lease_clear_timers(&client->lease->ia);
816
817         client->lease = sd_dhcp6_lease_unref(client->lease);
818         client->lease = lease;
819         lease = NULL;
820
821         return DHCP6_STATE_BOUND;
822 }
823
824 static int client_receive_advertise(sd_dhcp6_client *client,
825                                     DHCP6Message *advertise, size_t len) {
826         int r;
827         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
828         uint8_t pref_advertise = 0, pref_lease = 0;
829
830         if (advertise->type != DHCP6_ADVERTISE)
831                 return 0;
832
833         r = dhcp6_lease_new(&lease);
834         if (r < 0)
835                 return r;
836
837         r = client_parse_message(client, advertise, len, lease);
838         if (r < 0)
839                 return r;
840
841         r = dhcp6_lease_get_preference(lease, &pref_advertise);
842         if (r < 0)
843                 return r;
844
845         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
846         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
847                 sd_dhcp6_lease_unref(client->lease);
848                 client->lease = lease;
849                 lease = NULL;
850                 r = 0;
851         }
852
853         if (pref_advertise == 255 || client->retransmit_count > 1)
854                 r = DHCP6_STATE_REQUEST;
855
856         return r;
857 }
858
859 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
860                                   void *userdata) {
861         sd_dhcp6_client *client = userdata;
862         DHCP6_CLIENT_DONT_DESTROY(client);
863         _cleanup_free_ DHCP6Message *message;
864         int r, buflen, len;
865
866         assert(s);
867         assert(client);
868         assert(client->event);
869
870         r = ioctl(fd, FIONREAD, &buflen);
871         if (r < 0 || buflen <= 0)
872                 buflen = DHCP6_MIN_OPTIONS_SIZE;
873
874         message = malloc0(buflen);
875         if (!message)
876                 return -ENOMEM;
877
878         len = read(fd, message, buflen);
879         if ((size_t)len < sizeof(DHCP6Message)) {
880                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
881                 return 0;
882         }
883
884         switch(message->type) {
885         case DHCP6_SOLICIT:
886         case DHCP6_REQUEST:
887         case DHCP6_CONFIRM:
888         case DHCP6_RENEW:
889         case DHCP6_REBIND:
890         case DHCP6_RELEASE:
891         case DHCP6_DECLINE:
892         case DHCP6_INFORMATION_REQUEST:
893         case DHCP6_RELAY_FORW:
894         case DHCP6_RELAY_REPL:
895                 return 0;
896
897         case DHCP6_ADVERTISE:
898         case DHCP6_REPLY:
899         case DHCP6_RECONFIGURE:
900                 break;
901
902         default:
903                 log_dhcp6_client(client, "unknown message type %d",
904                                  message->type);
905                 return 0;
906         }
907
908         if (client->transaction_id != (message->transaction_id &
909                                        htobe32(0x00ffffff)))
910                 return 0;
911
912         switch (client->state) {
913         case DHCP6_STATE_SOLICITATION:
914                 r = client_receive_advertise(client, message, len);
915
916                 if (r == DHCP6_STATE_REQUEST) {
917                         client_start(client, r);
918
919                         break;
920                 }
921
922                 /* fall through for Soliciation Rapid Commit option check */
923         case DHCP6_STATE_REQUEST:
924         case DHCP6_STATE_RENEW:
925         case DHCP6_STATE_REBIND:
926
927                 r = client_receive_reply(client, message, len);
928                 if (r < 0)
929                         return 0;
930
931                 if (r == DHCP6_STATE_BOUND) {
932
933                         r = client_start(client, DHCP6_STATE_BOUND);
934                         if (r < 0) {
935                                 client_stop(client, r);
936                                 return 0;
937                         }
938
939                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
940                 }
941
942                 break;
943
944         case DHCP6_STATE_BOUND:
945
946                 break;
947
948         case DHCP6_STATE_STOPPED:
949                 return 0;
950         }
951
952         if (r >= 0) {
953                 log_dhcp6_client(client, "Recv %s",
954                                  dhcp6_message_type_to_string(message->type));
955         }
956
957         return 0;
958 }
959
960 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
961 {
962         int r;
963         usec_t timeout, time_now;
964         char time_string[FORMAT_TIMESPAN_MAX];
965
966         assert_return(client, -EINVAL);
967         assert_return(client->event, -EINVAL);
968         assert_return(client->index > 0, -EINVAL);
969         assert_return(client->state != state, -EINVAL);
970
971         client->timeout_resend_expire =
972                 sd_event_source_unref(client->timeout_resend_expire);
973         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
974         client->retransmit_time = 0;
975         client->retransmit_count = 0;
976
977         if (client->state == DHCP6_STATE_STOPPED) {
978                 time_now = now(clock_boottime_or_monotonic());
979         } else {
980                 r = sd_event_now(client->event, clock_boottime_or_monotonic(),
981                                  &time_now);
982                 if (r < 0)
983                         return r;
984         }
985
986         switch (state) {
987         case DHCP6_STATE_STOPPED:
988         case DHCP6_STATE_SOLICITATION:
989
990                 r = client_ensure_iaid(client);
991                 if (r < 0)
992                         return r;
993
994                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
995                 if (r < 0)
996                         return r;
997
998                 client->fd = r;
999
1000                 r = sd_event_add_io(client->event, &client->receive_message,
1001                                     client->fd, EPOLLIN, client_receive_message,
1002                                     client);
1003                 if (r < 0)
1004                         return r;
1005
1006                 r = sd_event_source_set_priority(client->receive_message,
1007                                                  client->event_priority);
1008                 if (r < 0)
1009                         return r;
1010
1011                 r = sd_event_source_set_description(client->receive_message, "dhcp6-receive-message");
1012                 if (r < 0)
1013                         return r;
1014
1015                 client->state = DHCP6_STATE_SOLICITATION;
1016
1017                 break;
1018
1019         case DHCP6_STATE_REQUEST:
1020         case DHCP6_STATE_RENEW:
1021         case DHCP6_STATE_REBIND:
1022
1023                 client->state = state;
1024
1025                 break;
1026
1027         case DHCP6_STATE_BOUND:
1028
1029                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
1030                     client->lease->ia.lifetime_t2 == 0xffffffff) {
1031
1032                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
1033                                          be32toh(client->lease->ia.lifetime_t1),
1034                                          be32toh(client->lease->ia.lifetime_t2));
1035
1036                         return 0;
1037                 }
1038
1039                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
1040
1041                 log_dhcp6_client(client, "T1 expires in %s",
1042                                  format_timespan(time_string,
1043                                                  FORMAT_TIMESPAN_MAX,
1044                                                  timeout, 0));
1045
1046                 r = sd_event_add_time(client->event,
1047                                       &client->lease->ia.timeout_t1,
1048                                       clock_boottime_or_monotonic(), time_now + timeout,
1049                                       10 * USEC_PER_SEC, client_timeout_t1,
1050                                       client);
1051                 if (r < 0)
1052                         return r;
1053
1054                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
1055                                                  client->event_priority);
1056                 if (r < 0)
1057                         return r;
1058
1059                 r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout");
1060                 if (r < 0)
1061                         return r;
1062
1063                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
1064
1065                 log_dhcp6_client(client, "T2 expires in %s",
1066                                  format_timespan(time_string,
1067                                                  FORMAT_TIMESPAN_MAX,
1068                                                  timeout, 0));
1069
1070                 r = sd_event_add_time(client->event,
1071                                       &client->lease->ia.timeout_t2,
1072                                       clock_boottime_or_monotonic(), time_now + timeout,
1073                                       10 * USEC_PER_SEC, client_timeout_t2,
1074                                       client);
1075                 if (r < 0)
1076                         return r;
1077
1078                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
1079                                                  client->event_priority);
1080                 if (r < 0)
1081                         return r;
1082
1083                 r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout");
1084                 if (r < 0)
1085                         return r;
1086
1087                 client->state = state;
1088
1089                 return 0;
1090         }
1091
1092         client->transaction_id = random_u32() & htobe32(0x00ffffff);
1093         client->transaction_start = time_now;
1094
1095         r = sd_event_add_time(client->event, &client->timeout_resend,
1096                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
1097                               client);
1098         if (r < 0)
1099                 return r;
1100
1101         r = sd_event_source_set_priority(client->timeout_resend,
1102                                          client->event_priority);
1103         if (r < 0)
1104                 return r;
1105
1106         r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout");
1107         if (r < 0)
1108                 return r;
1109
1110         return 0;
1111 }
1112
1113 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1114 {
1115         client_stop(client, DHCP6_EVENT_STOP);
1116
1117         return 0;
1118 }
1119
1120 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1121 {
1122         int r = 0;
1123
1124         assert_return(client, -EINVAL);
1125         assert_return(client->event, -EINVAL);
1126         assert_return(client->index > 0, -EINVAL);
1127
1128         r = client_reset(client);
1129         if (r < 0)
1130                 return r;
1131
1132         return client_start(client, DHCP6_STATE_SOLICITATION);
1133 }
1134
1135 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1136                                  int priority)
1137 {
1138         int r;
1139
1140         assert_return(client, -EINVAL);
1141         assert_return(!client->event, -EBUSY);
1142
1143         if (event)
1144                 client->event = sd_event_ref(event);
1145         else {
1146                 r = sd_event_default(&client->event);
1147                 if (r < 0)
1148                         return 0;
1149         }
1150
1151         client->event_priority = priority;
1152
1153         return 0;
1154 }
1155
1156 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1157         assert_return(client, -EINVAL);
1158
1159         client->event = sd_event_unref(client->event);
1160
1161         return 0;
1162 }
1163
1164 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1165         if (!client)
1166                 return NULL;
1167
1168         return client->event;
1169 }
1170
1171 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1172         if (client)
1173                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1174
1175         return client;
1176 }
1177
1178 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1179         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1180                 client_reset(client);
1181
1182                 sd_dhcp6_client_detach_event(client);
1183
1184                 free(client->req_opts);
1185                 free(client);
1186
1187                 return NULL;
1188         }
1189
1190         return client;
1191 }
1192
1193 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1194 {
1195         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1196         sd_id128_t machine_id;
1197         int r;
1198         size_t t;
1199
1200         assert_return(ret, -EINVAL);
1201
1202         client = new0(sd_dhcp6_client, 1);
1203         if (!client)
1204                 return -ENOMEM;
1205
1206         client->n_ref = REFCNT_INIT;
1207
1208         client->ia_na.type = DHCP6_OPTION_IA_NA;
1209
1210         client->index = -1;
1211
1212         client->fd = -1;
1213
1214         /* initialize DUID */
1215         client->duid.en.type = htobe16(DHCP6_DUID_EN);
1216         client->duid.en.pen = htobe32(SYSTEMD_PEN);
1217         client->duid_len = sizeof(client->duid.en);
1218
1219         r = sd_id128_get_machine(&machine_id);
1220         if (r < 0)
1221                 return r;
1222
1223         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1224            directly */
1225         siphash24(client->duid.en.id, &machine_id, sizeof(machine_id), HASH_KEY.bytes);
1226
1227         client->req_opts_len = ELEMENTSOF(default_req_opts);
1228
1229         client->req_opts = new0(be16_t, client->req_opts_len);
1230         if (!client->req_opts)
1231                 return -ENOMEM;
1232
1233         for (t = 0; t < client->req_opts_len; t++)
1234                 client->req_opts[t] = htobe16(default_req_opts[t]);
1235
1236         *ret = client;
1237         client = NULL;
1238
1239         return 0;
1240 }