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