chiark / gitweb /
sd-dhcp6-client: Add Option Request Option support
[elogind.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 Intel Corporation. All rights reserved.
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25
26 #include "udev.h"
27 #include "udev-util.h"
28 #include "virt.h"
29 #include "siphash24.h"
30 #include "util.h"
31 #include "refcnt.h"
32
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
38
39 #define SYSTEMD_PEN 43793
40 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
41
42 struct sd_dhcp6_client {
43         RefCount n_ref;
44
45         enum DHCP6State state;
46         sd_event *event;
47         int event_priority;
48         int index;
49         struct ether_addr mac_addr;
50         DHCP6IA ia_na;
51         be32_t transaction_id;
52         struct sd_dhcp6_lease *lease;
53         int fd;
54         be16_t *req_opts;
55         size_t req_opts_allocated;
56         size_t req_opts_len;
57         sd_event_source *receive_message;
58         usec_t retransmit_time;
59         uint8_t retransmit_count;
60         sd_event_source *timeout_resend;
61         sd_event_source *timeout_resend_expire;
62         sd_dhcp6_client_cb_t cb;
63         void *userdata;
64
65         struct duid_en {
66                 uint16_t type; /* DHCP6_DUID_EN */
67                 uint32_t pen;
68                 uint8_t id[8];
69         } _packed_ duid;
70 };
71
72 static const uint16_t default_req_opts[] = {
73         DHCP6_OPTION_DNS_SERVERS,
74         DHCP6_OPTION_DOMAIN_LIST,
75         DHCP6_OPTION_NTP_SERVER,
76 };
77
78 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
79         [DHCP6_SOLICIT] = "SOLICIT",
80         [DHCP6_ADVERTISE] = "ADVERTISE",
81         [DHCP6_REQUEST] = "REQUEST",
82         [DHCP6_CONFIRM] = "CONFIRM",
83         [DHCP6_RENEW] = "RENEW",
84         [DHCP6_REBIND] = "REBIND",
85         [DHCP6_REPLY] = "REPLY",
86         [DHCP6_RELEASE] = "RELEASE",
87         [DHCP6_DECLINE] = "DECLINE",
88         [DHCP6_RECONFIGURE] = "RECONFIGURE",
89         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
90         [DHCP6_RELAY_FORW] = "RELAY-FORW",
91         [DHCP6_RELAY_REPL] = "RELAY-REPL",
92 };
93
94 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
95
96 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
97         [DHCP6_STATUS_SUCCESS] = "Success",
98         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
99         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
100         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
101         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
102         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
103 };
104
105 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
106
107 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
108 #define _cleanup_dhcp6_client_unref_ _cleanup_(sd_dhcp6_client_unrefp)
109
110 #define DHCP6_CLIENT_DONT_DESTROY(client) \
111         _cleanup_dhcp6_client_unref_ _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
112
113 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
114
115 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
116                                  sd_dhcp6_client_cb_t cb, void *userdata)
117 {
118         assert_return(client, -EINVAL);
119
120         client->cb = cb;
121         client->userdata = userdata;
122
123         return 0;
124 }
125
126 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
127 {
128         assert_return(client, -EINVAL);
129         assert_return(interface_index >= -1, -EINVAL);
130
131         client->index = interface_index;
132
133         return 0;
134 }
135
136 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
137                             const struct ether_addr *mac_addr)
138 {
139         assert_return(client, -EINVAL);
140
141         if (mac_addr)
142                 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
143         else
144                 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
145
146         return 0;
147 }
148
149 int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client,
150                                        uint16_t option) {
151         size_t t;
152
153         assert_return(client, -EINVAL);
154         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
155
156         switch(option) {
157         case DHCP6_OPTION_DNS_SERVERS:
158         case DHCP6_OPTION_DOMAIN_LIST:
159         case DHCP6_OPTION_SNTP_SERVERS:
160         case DHCP6_OPTION_NTP_SERVER:
161                 break;
162
163         default:
164                 return -EINVAL;
165         }
166
167         for (t = 0; t < client->req_opts_len; t++)
168                 if (client->req_opts[t] == htobe16(option))
169                         return -EEXIST;
170
171         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
172                             client->req_opts_len + 1))
173                 return -ENOMEM;
174
175         client->req_opts[client->req_opts_len++] = htobe16(option);
176
177         return 0;
178 }
179
180 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
181         assert_return(client, -EINVAL);
182         assert_return(ret, -EINVAL);
183
184         if (!client->lease)
185                 return -ENOMSG;
186
187         *ret = sd_dhcp6_lease_ref(client->lease);
188
189         return 0;
190 }
191
192 static void client_notify(sd_dhcp6_client *client, int event) {
193         if (client->cb)
194                 client->cb(client, event, client->userdata);
195 }
196
197 static int client_reset(sd_dhcp6_client *client) {
198         assert_return(client, -EINVAL);
199
200         client->receive_message =
201                 sd_event_source_unref(client->receive_message);
202
203         client->fd = safe_close(client->fd);
204
205         client->transaction_id = 0;
206
207         client->ia_na.timeout_t1 =
208                 sd_event_source_unref(client->ia_na.timeout_t1);
209         client->ia_na.timeout_t2 =
210                 sd_event_source_unref(client->ia_na.timeout_t2);
211
212         client->retransmit_time = 0;
213         client->retransmit_count = 0;
214         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
215         client->timeout_resend_expire =
216                 sd_event_source_unref(client->timeout_resend_expire);
217
218         client->state = DHCP6_STATE_STOPPED;
219
220         return 0;
221 }
222
223 static void client_stop(sd_dhcp6_client *client, int error) {
224         DHCP6_CLIENT_DONT_DESTROY(client);
225
226         assert(client);
227
228         client_notify(client, error);
229
230         client_reset(client);
231 }
232
233 static int client_send_message(sd_dhcp6_client *client) {
234         _cleanup_free_ DHCP6Message *message = NULL;
235         struct in6_addr all_servers =
236                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
237         size_t len, optlen = 512;
238         uint8_t *opt;
239         int r;
240
241         len = sizeof(DHCP6Message) + optlen;
242
243         message = malloc0(len);
244         if (!message)
245                 return -ENOMEM;
246
247         opt = (uint8_t *)(message + 1);
248
249         message->transaction_id = client->transaction_id;
250
251         switch(client->state) {
252         case DHCP6_STATE_SOLICITATION:
253                 message->type = DHCP6_SOLICIT;
254
255                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
256                 if (r < 0)
257                         return r;
258
259                 break;
260
261         case DHCP6_STATE_REQUEST:
262                 message->type = DHCP6_REQUEST;
263
264                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
265                                         client->lease->serverid_len,
266                                         client->lease->serverid);
267                 if (r < 0)
268                         return r;
269
270                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
271                 if (r < 0)
272                         return r;
273
274                 break;
275
276         case DHCP6_STATE_STOPPED:
277         case DHCP6_STATE_RS:
278         case DHCP6_STATE_BOUND:
279                 return -EINVAL;
280         }
281
282         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
283                                 client->req_opts_len * sizeof(be16_t),
284                                 client->req_opts);
285         if (r < 0)
286                 return r;
287
288         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
289                                 sizeof(client->duid), &client->duid);
290         if (r < 0)
291                 return r;
292
293         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
294                                           len - optlen);
295         if (r < 0)
296                 return r;
297
298         log_dhcp6_client(client, "Sent %s",
299                          dhcp6_message_type_to_string(message->type));
300
301         return 0;
302 }
303
304 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
305                              void *userdata) {
306         sd_dhcp6_client *client = userdata;
307
308         assert_return(s, -EINVAL);
309         assert_return(client, -EINVAL);
310         assert_return(client->lease, -EINVAL);
311
312         client->lease->ia.timeout_t2 =
313                 sd_event_source_unref(client->lease->ia.timeout_t2);
314
315         log_dhcp6_client(client, "Timeout T2");
316
317         return 0;
318 }
319
320 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
321                              void *userdata) {
322         sd_dhcp6_client *client = userdata;
323
324         assert_return(s, -EINVAL);
325         assert_return(client, -EINVAL);
326         assert_return(client->lease, -EINVAL);
327
328         client->lease->ia.timeout_t1 =
329                 sd_event_source_unref(client->lease->ia.timeout_t1);
330
331         log_dhcp6_client(client, "Timeout T1");
332
333         return 0;
334 }
335
336 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
337                                         void *userdata) {
338         sd_dhcp6_client *client = userdata;
339
340         assert(s);
341         assert(client);
342         assert(client->event);
343
344         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
345
346         return 0;
347 }
348
349 static usec_t client_timeout_compute_random(usec_t val) {
350         return val - val / 10 +
351                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
352 }
353
354 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
355                                  void *userdata) {
356         int r = 0;
357         sd_dhcp6_client *client = userdata;
358         usec_t time_now, init_retransmit_time, max_retransmit_time;
359         usec_t max_retransmit_duration;
360         uint8_t max_retransmit_count = 0;
361         char time_string[FORMAT_TIMESPAN_MAX];
362
363         assert(s);
364         assert(client);
365         assert(client->event);
366
367         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
368
369         switch (client->state) {
370         case DHCP6_STATE_SOLICITATION:
371
372                 if (client->retransmit_count && client->lease) {
373                         client_start(client, DHCP6_STATE_REQUEST);
374                         return 0;
375                 }
376
377                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
378                 max_retransmit_time = DHCP6_SOL_MAX_RT;
379                 max_retransmit_count = 0;
380                 max_retransmit_duration = 0;
381
382                 break;
383
384         case DHCP6_STATE_REQUEST:
385                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
386                 max_retransmit_time = DHCP6_REQ_MAX_RT;
387                 max_retransmit_count = DHCP6_REQ_MAX_RC;
388                 max_retransmit_duration = 0;
389
390                 break;
391
392         case DHCP6_STATE_STOPPED:
393         case DHCP6_STATE_RS:
394         case DHCP6_STATE_BOUND:
395                 return 0;
396         }
397
398         if (max_retransmit_count &&
399             client->retransmit_count >= max_retransmit_count) {
400                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
401                 return 0;
402         }
403
404         r = client_send_message(client);
405         if (r >= 0)
406                 client->retransmit_count++;
407
408
409         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
410         if (r < 0)
411                 goto error;
412
413         if (!client->retransmit_time) {
414                 client->retransmit_time =
415                         client_timeout_compute_random(init_retransmit_time);
416
417                 if (client->state == DHCP6_STATE_SOLICITATION)
418                         client->retransmit_time += init_retransmit_time / 10;
419
420         } else {
421                 if (max_retransmit_time &&
422                     client->retransmit_time > max_retransmit_time / 2)
423                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
424                 else
425                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
426         }
427
428         log_dhcp6_client(client, "Next retransmission in %s",
429                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
430                                          client->retransmit_time, 0));
431
432         r = sd_event_add_time(client->event, &client->timeout_resend,
433                               CLOCK_MONOTONIC,
434                               time_now + client->retransmit_time,
435                               10 * USEC_PER_MSEC, client_timeout_resend,
436                               client);
437         if (r < 0)
438                 goto error;
439
440         r = sd_event_source_set_priority(client->timeout_resend,
441                                          client->event_priority);
442         if (r < 0)
443                 goto error;
444
445         if (max_retransmit_duration && !client->timeout_resend_expire) {
446
447                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
448                                  max_retransmit_duration / USEC_PER_SEC);
449
450                 r = sd_event_add_time(client->event,
451                                       &client->timeout_resend_expire,
452                                       CLOCK_MONOTONIC,
453                                       time_now + max_retransmit_duration,
454                                       USEC_PER_SEC,
455                                       client_timeout_resend_expire, client);
456                 if (r < 0)
457                         goto error;
458
459                 r = sd_event_source_set_priority(client->timeout_resend_expire,
460                                                  client->event_priority);
461                 if (r < 0)
462                         goto error;
463         }
464
465 error:
466         if (r < 0)
467                 client_stop(client, r);
468
469         return 0;
470 }
471
472 static int client_ensure_iaid(sd_dhcp6_client *client) {
473         const char *name = NULL;
474         uint64_t id;
475
476         assert(client);
477
478         if (client->ia_na.id)
479                 return 0;
480
481         if (detect_container(NULL) <= 0) {
482                 /* not in a container, udev will be around */
483                 _cleanup_udev_unref_ struct udev *udev;
484                 _cleanup_udev_device_unref_ struct udev_device *device = NULL;
485                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
486
487                 udev = udev_new();
488                 if (!udev)
489                         return -ENOMEM;
490
491                 sprintf(ifindex_str, "n%d", client->index);
492                 device = udev_device_new_from_device_id(udev, ifindex_str);
493                 if (!device)
494                         return -errno;
495
496                 if (udev_device_get_is_initialized(device) <= 0)
497                         /* not yet ready */
498                         return -EBUSY;
499
500                 name = net_get_name(device);
501         }
502
503         if (name)
504                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
505         else
506                 /* fall back to mac address if no predictable name available */
507                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
508                           HASH_KEY.bytes);
509
510         /* fold into 32 bits */
511         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
512
513         return 0;
514 }
515
516 static int client_parse_message(sd_dhcp6_client *client,
517                                 DHCP6Message *message, size_t len,
518                                 sd_dhcp6_lease *lease) {
519         int r;
520         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
521         uint16_t optcode, status;
522         size_t optlen, id_len;
523         bool clientid = false;
524         be32_t iaid_lease;
525
526         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
527                                        &optval)) >= 0) {
528                 switch (optcode) {
529                 case DHCP6_OPTION_CLIENTID:
530                         if (clientid) {
531                                 log_dhcp6_client(client, "%s contains multiple clientids",
532                                                  dhcp6_message_type_to_string(message->type));
533                                 return -EINVAL;
534                         }
535
536                         if (optlen != sizeof(client->duid) ||
537                             memcmp(&client->duid, optval, optlen) != 0) {
538                                 log_dhcp6_client(client, "%s DUID does not match",
539                                                  dhcp6_message_type_to_string(message->type));
540
541                                 return -EINVAL;
542                         }
543                         clientid = true;
544
545                         break;
546
547                 case DHCP6_OPTION_SERVERID:
548                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
549                         if (r >= 0 && id) {
550                                 log_dhcp6_client(client, "%s contains multiple serverids",
551                                                  dhcp6_message_type_to_string(message->type));
552                                 return -EINVAL;
553                         }
554
555                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
556                         if (r < 0)
557                                 return r;
558
559                         break;
560
561                 case DHCP6_OPTION_PREFERENCE:
562                         if (optlen != 1)
563                                 return -EINVAL;
564
565                         r = dhcp6_lease_set_preference(lease, *optval);
566                         if (r < 0)
567                                 return r;
568
569                         break;
570
571                 case DHCP6_OPTION_STATUS_CODE:
572                         if (optlen < 2)
573                                 return -EINVAL;
574
575                         status = optval[0] << 8 | optval[1];
576                         if (status) {
577                                 log_dhcp6_client(client, "%s Status %s",
578                                                  dhcp6_message_type_to_string(message->type),
579                                                  dhcp6_message_status_to_string(status));
580                                 return -EINVAL;
581                         }
582
583                         break;
584
585                 case DHCP6_OPTION_IA_NA:
586                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
587                                                   &lease->ia);
588                         if (r < 0 && r != -ENOMSG)
589                                 return r;
590
591                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
592                         if (r < 0)
593                                 return r;
594
595                         if (client->ia_na.id != iaid_lease) {
596                                 log_dhcp6_client(client, "%s has wrong IAID",
597                                                  dhcp6_message_type_to_string(message->type));
598                                 return -EINVAL;
599                         }
600
601                         break;
602                 }
603         }
604
605         if ((r < 0 && r != -ENOMSG) || !clientid) {
606                 log_dhcp6_client(client, "%s has incomplete options",
607                                  dhcp6_message_type_to_string(message->type));
608                 return -EINVAL;
609         }
610
611         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
612         if (r < 0)
613                 log_dhcp6_client(client, "%s has no server id",
614                                  dhcp6_message_type_to_string(message->type));
615
616         return r;
617 }
618
619 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
620                                 size_t len)
621 {
622         int r;
623         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
624
625         if (reply->type != DHCP6_REPLY)
626                 return -EINVAL;
627
628         r = dhcp6_lease_new(&lease);
629         if (r < 0)
630                 return -ENOMEM;
631
632         r = client_parse_message(client, reply, len, lease);
633         if (r < 0)
634                 return r;
635
636         dhcp6_lease_clear_timers(&client->lease->ia);
637
638         client->lease = sd_dhcp6_lease_unref(client->lease);
639         client->lease = lease;
640         lease = NULL;
641
642         return DHCP6_STATE_BOUND;
643 }
644
645 static int client_receive_advertise(sd_dhcp6_client *client,
646                                     DHCP6Message *advertise, size_t len) {
647         int r;
648         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
649         uint8_t pref_advertise = 0, pref_lease = 0;
650
651         if (advertise->type != DHCP6_ADVERTISE)
652                 return -EINVAL;
653
654         r = dhcp6_lease_new(&lease);
655         if (r < 0)
656                 return r;
657
658         r = client_parse_message(client, advertise, len, lease);
659         if (r < 0)
660                 return r;
661
662         r = dhcp6_lease_get_preference(lease, &pref_advertise);
663         if (r < 0)
664                 return r;
665
666         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
667         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
668                 sd_dhcp6_lease_unref(client->lease);
669                 client->lease = lease;
670                 lease = NULL;
671                 r = 0;
672         }
673
674         if (pref_advertise == 255 || client->retransmit_count > 1)
675                 r = DHCP6_STATE_REQUEST;
676
677         return r;
678 }
679
680 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
681                                   void *userdata) {
682         sd_dhcp6_client *client = userdata;
683         DHCP6_CLIENT_DONT_DESTROY(client);
684         _cleanup_free_ DHCP6Message *message;
685         int r, buflen, len;
686
687         assert(s);
688         assert(client);
689         assert(client->event);
690
691         r = ioctl(fd, FIONREAD, &buflen);
692         if (r < 0 || buflen <= 0)
693                 buflen = DHCP6_MIN_OPTIONS_SIZE;
694
695         message = malloc0(buflen);
696         if (!message)
697                 return -ENOMEM;
698
699         len = read(fd, message, buflen);
700         if ((size_t)len < sizeof(DHCP6Message)) {
701                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
702                 return 0;
703         }
704
705         switch(message->type) {
706         case DHCP6_SOLICIT:
707         case DHCP6_REQUEST:
708         case DHCP6_CONFIRM:
709         case DHCP6_RENEW:
710         case DHCP6_REBIND:
711         case DHCP6_RELEASE:
712         case DHCP6_DECLINE:
713         case DHCP6_INFORMATION_REQUEST:
714         case DHCP6_RELAY_FORW:
715         case DHCP6_RELAY_REPL:
716                 return 0;
717
718         case DHCP6_ADVERTISE:
719         case DHCP6_REPLY:
720         case DHCP6_RECONFIGURE:
721                 break;
722
723         default:
724                 log_dhcp6_client(client, "unknown message type %d",
725                                  message->type);
726                 return 0;
727         }
728
729         if (client->transaction_id != (message->transaction_id &
730                                        htobe32(0x00ffffff)))
731                 return 0;
732
733         switch (client->state) {
734         case DHCP6_STATE_SOLICITATION:
735                 r = client_receive_advertise(client, message, len);
736
737                 if (r == DHCP6_STATE_REQUEST)
738                         client_start(client, r);
739
740                 break;
741
742         case DHCP6_STATE_REQUEST:
743                 r = client_receive_reply(client, message, len);
744                 if (r < 0)
745                         return 0;
746
747                 if (r == DHCP6_STATE_BOUND) {
748
749                         r = client_start(client, DHCP6_STATE_BOUND);
750                         if (r < 0) {
751                                 client_stop(client, r);
752                                 return 0;
753                         }
754
755                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
756                 }
757
758                 break;
759
760         case DHCP6_STATE_BOUND:
761
762                 break;
763
764         case DHCP6_STATE_STOPPED:
765         case DHCP6_STATE_RS:
766                 return 0;
767         }
768
769         if (r >= 0) {
770                 log_dhcp6_client(client, "Recv %s",
771                                  dhcp6_message_type_to_string(message->type));
772         }
773
774         return 0;
775 }
776
777 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
778 {
779         int r;
780         usec_t timeout, time_now;
781         char time_string[FORMAT_TIMESPAN_MAX];
782
783         assert_return(client, -EINVAL);
784         assert_return(client->event, -EINVAL);
785         assert_return(client->index > 0, -EINVAL);
786         assert_return(client->state != state, -EINVAL);
787
788         client->timeout_resend_expire =
789                 sd_event_source_unref(client->timeout_resend_expire);
790         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
791         client->retransmit_time = 0;
792         client->retransmit_count = 0;
793
794         switch (state) {
795         case DHCP6_STATE_STOPPED:
796         case DHCP6_STATE_RS:
797         case DHCP6_STATE_SOLICITATION:
798
799                 r = client_ensure_iaid(client);
800                 if (r < 0)
801                         return r;
802
803                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
804                 if (r < 0)
805                         return r;
806
807                 client->fd = r;
808
809                 r = sd_event_add_io(client->event, &client->receive_message,
810                                     client->fd, EPOLLIN, client_receive_message,
811                                     client);
812                 if (r < 0)
813                         return r;
814
815                 r = sd_event_source_set_priority(client->receive_message,
816                                                  client->event_priority);
817                 if (r < 0)
818                         return r;
819
820                 client->state = DHCP6_STATE_SOLICITATION;
821
822                 break;
823
824         case DHCP6_STATE_REQUEST:
825
826                 client->state = state;
827
828                 break;
829
830         case DHCP6_STATE_BOUND:
831
832                 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
833                 if (r < 0)
834                         return r;
835
836                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
837                     client->lease->ia.lifetime_t2 == 0xffffffff) {
838
839                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
840                                          be32toh(client->lease->ia.lifetime_t1),
841                                          be32toh(client->lease->ia.lifetime_t2));
842
843                         return 0;
844                 }
845
846                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
847
848                 log_dhcp6_client(client, "T1 expires in %s",
849                                  format_timespan(time_string,
850                                                  FORMAT_TIMESPAN_MAX,
851                                                  timeout, 0));
852
853                 r = sd_event_add_time(client->event,
854                                       &client->lease->ia.timeout_t1,
855                                       CLOCK_MONOTONIC, time_now + timeout,
856                                       10 * USEC_PER_SEC, client_timeout_t1,
857                                       client);
858                 if (r < 0)
859                         return r;
860
861                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
862                                                  client->event_priority);
863                 if (r < 0)
864                         return r;
865
866                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
867
868                 log_dhcp6_client(client, "T2 expires in %s",
869                                  format_timespan(time_string,
870                                                  FORMAT_TIMESPAN_MAX,
871                                                  timeout, 0));
872
873                 r = sd_event_add_time(client->event,
874                                       &client->lease->ia.timeout_t2,
875                                       CLOCK_MONOTONIC, time_now + timeout,
876                                       10 * USEC_PER_SEC, client_timeout_t2,
877                                       client);
878                 if (r < 0)
879                         return r;
880
881                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
882                                                  client->event_priority);
883                 if (r < 0)
884                         return r;
885
886                 return 0;
887         }
888
889         client->transaction_id = random_u32() & htobe32(0x00ffffff);
890
891         r = sd_event_add_time(client->event, &client->timeout_resend,
892                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
893                               client);
894         if (r < 0)
895                 return r;
896
897         r = sd_event_source_set_priority(client->timeout_resend,
898                                          client->event_priority);
899         if (r < 0)
900                 return r;
901
902         return 0;
903 }
904
905 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
906 {
907         client_stop(client, DHCP6_EVENT_STOP);
908
909         return 0;
910 }
911
912 int sd_dhcp6_client_start(sd_dhcp6_client *client)
913 {
914         int r = 0;
915
916         assert_return(client, -EINVAL);
917         assert_return(client->event, -EINVAL);
918         assert_return(client->index > 0, -EINVAL);
919
920         r = client_reset(client);
921         if (r < 0)
922                 return r;
923
924         return client_start(client, DHCP6_STATE_SOLICITATION);
925 }
926
927 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
928                                  int priority)
929 {
930         int r;
931
932         assert_return(client, -EINVAL);
933         assert_return(!client->event, -EBUSY);
934
935         if (event)
936                 client->event = sd_event_ref(event);
937         else {
938                 r = sd_event_default(&client->event);
939                 if (r < 0)
940                         return 0;
941         }
942
943         client->event_priority = priority;
944
945         return 0;
946 }
947
948 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
949         assert_return(client, -EINVAL);
950
951         client->event = sd_event_unref(client->event);
952
953         return 0;
954 }
955
956 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
957         if (!client)
958                 return NULL;
959
960         return client->event;
961 }
962
963 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
964         if (client)
965                 assert_se(REFCNT_INC(client->n_ref) >= 2);
966
967         return client;
968 }
969
970 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
971         if (client && REFCNT_DEC(client->n_ref) <= 0) {
972                 client_reset(client);
973
974                 sd_dhcp6_client_detach_event(client);
975
976                 free(client->req_opts);
977                 free(client);
978
979                 return NULL;
980         }
981
982         return client;
983 }
984
985 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
986 {
987         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
988         sd_id128_t machine_id;
989         int r;
990         size_t t;
991
992         assert_return(ret, -EINVAL);
993
994         client = new0(sd_dhcp6_client, 1);
995         if (!client)
996                 return -ENOMEM;
997
998         client->n_ref = REFCNT_INIT;
999
1000         client->ia_na.type = DHCP6_OPTION_IA_NA;
1001
1002         client->index = -1;
1003
1004         client->fd = -1;
1005
1006         /* initialize DUID */
1007         client->duid.type = htobe16(DHCP6_DUID_EN);
1008         client->duid.pen = htobe32(SYSTEMD_PEN);
1009
1010         r = sd_id128_get_machine(&machine_id);
1011         if (r < 0)
1012                 return r;
1013
1014         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1015            directly */
1016         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1017                   HASH_KEY.bytes);
1018
1019         client->req_opts_len = ELEMENTSOF(default_req_opts);
1020
1021         client->req_opts = new0(be16_t, client->req_opts_len);
1022         if (!client->req_opts)
1023                 return -ENOMEM;
1024
1025         for (t = 0; t < client->req_opts_len; t++)
1026                 client->req_opts[t] = htobe16(default_req_opts[t]);
1027
1028         *ret = client;
1029         client = NULL;
1030
1031         return 0;
1032 }