chiark / gitweb /
Fix a few more typos
[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(&opt, &optlen,
256                                         DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
257                 if (r < 0)
258                         return r;
259
260                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
261                 if (r < 0)
262                         return r;
263
264                 break;
265
266         case DHCP6_STATE_REQUEST:
267         case DHCP6_STATE_RENEW:
268
269                 if (client->state == DHCP6_STATE_REQUEST)
270                         message->type = DHCP6_REQUEST;
271                 else
272                         message->type = DHCP6_RENEW;
273
274                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
275                                         client->lease->serverid_len,
276                                         client->lease->serverid);
277                 if (r < 0)
278                         return r;
279
280                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
281                 if (r < 0)
282                         return r;
283
284                 break;
285
286         case DHCP6_STATE_REBIND:
287                 message->type = DHCP6_REBIND;
288
289                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
290                 if (r < 0)
291                         return r;
292
293                 break;
294
295         case DHCP6_STATE_STOPPED:
296         case DHCP6_STATE_BOUND:
297                 return -EINVAL;
298         }
299
300         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_ORO,
301                                 client->req_opts_len * sizeof(be16_t),
302                                 client->req_opts);
303         if (r < 0)
304                 return r;
305
306         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
307                                 sizeof(client->duid), &client->duid);
308         if (r < 0)
309                 return r;
310
311         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
312                                           len - optlen);
313         if (r < 0)
314                 return r;
315
316         log_dhcp6_client(client, "Sent %s",
317                          dhcp6_message_type_to_string(message->type));
318
319         return 0;
320 }
321
322 static int client_timeout_t2(sd_event_source *s, uint64_t usec,
323                              void *userdata) {
324         sd_dhcp6_client *client = userdata;
325
326         assert_return(s, -EINVAL);
327         assert_return(client, -EINVAL);
328         assert_return(client->lease, -EINVAL);
329
330         client->lease->ia.timeout_t2 =
331                 sd_event_source_unref(client->lease->ia.timeout_t2);
332
333         log_dhcp6_client(client, "Timeout T2");
334
335         client_start(client, DHCP6_STATE_REBIND);
336
337         return 0;
338 }
339
340 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
341                              void *userdata) {
342         sd_dhcp6_client *client = userdata;
343
344         assert_return(s, -EINVAL);
345         assert_return(client, -EINVAL);
346         assert_return(client->lease, -EINVAL);
347
348         client->lease->ia.timeout_t1 =
349                 sd_event_source_unref(client->lease->ia.timeout_t1);
350
351         log_dhcp6_client(client, "Timeout T1");
352
353         client_start(client, DHCP6_STATE_RENEW);
354
355         return 0;
356 }
357
358 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
359                                         void *userdata) {
360         sd_dhcp6_client *client = userdata;
361         DHCP6_CLIENT_DONT_DESTROY(client);
362         enum DHCP6State state;
363
364         assert(s);
365         assert(client);
366         assert(client->event);
367
368         state = client->state;
369
370         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
371
372         /* RFC 3315, section 18.1.4., says that "...the client may choose to
373            use a Solicit message to locate a new DHCP server..." */
374         if (state == DHCP6_STATE_REBIND)
375                 client_start(client, DHCP6_STATE_SOLICITATION);
376
377         return 0;
378 }
379
380 static usec_t client_timeout_compute_random(usec_t val) {
381         return val - val / 10 +
382                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
383 }
384
385 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
386                                  void *userdata) {
387         int r = 0;
388         sd_dhcp6_client *client = userdata;
389         usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
390         usec_t max_retransmit_duration = 0;
391         uint8_t max_retransmit_count = 0;
392         char time_string[FORMAT_TIMESPAN_MAX];
393         uint32_t expire = 0;
394
395         assert(s);
396         assert(client);
397         assert(client->event);
398
399         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
400
401         switch (client->state) {
402         case DHCP6_STATE_SOLICITATION:
403
404                 if (client->retransmit_count && client->lease) {
405                         client_start(client, DHCP6_STATE_REQUEST);
406                         return 0;
407                 }
408
409                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
410                 max_retransmit_time = DHCP6_SOL_MAX_RT;
411
412                 break;
413
414         case DHCP6_STATE_REQUEST:
415                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
416                 max_retransmit_time = DHCP6_REQ_MAX_RT;
417                 max_retransmit_count = DHCP6_REQ_MAX_RC;
418
419                 break;
420
421         case DHCP6_STATE_RENEW:
422                 init_retransmit_time = DHCP6_REN_TIMEOUT;
423                 max_retransmit_time = DHCP6_REN_MAX_RT;
424
425                 /* RFC 3315, section 18.1.3. says max retransmit duration will
426                    be the remaining time until T2. Instead of setting MRD,
427                    wait for T2 to trigger with the same end result */
428
429                 break;
430
431         case DHCP6_STATE_REBIND:
432                 init_retransmit_time = DHCP6_REB_TIMEOUT;
433                 max_retransmit_time = DHCP6_REB_MAX_RT;
434
435                 if (!client->timeout_resend_expire) {
436                         r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
437                                                          &expire);
438                         if (r < 0) {
439                                 client_stop(client, r);
440                                 return 0;
441                         }
442                         max_retransmit_duration = expire * USEC_PER_SEC;
443                 }
444
445                 break;
446
447         case DHCP6_STATE_STOPPED:
448         case DHCP6_STATE_BOUND:
449                 return 0;
450         }
451
452         if (max_retransmit_count &&
453             client->retransmit_count >= max_retransmit_count) {
454                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
455                 return 0;
456         }
457
458         r = client_send_message(client);
459         if (r >= 0)
460                 client->retransmit_count++;
461
462
463         r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
464         if (r < 0)
465                 goto error;
466
467         if (!client->retransmit_time) {
468                 client->retransmit_time =
469                         client_timeout_compute_random(init_retransmit_time);
470
471                 if (client->state == DHCP6_STATE_SOLICITATION)
472                         client->retransmit_time += init_retransmit_time / 10;
473
474         } else {
475                 if (max_retransmit_time &&
476                     client->retransmit_time > max_retransmit_time / 2)
477                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
478                 else
479                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
480         }
481
482         log_dhcp6_client(client, "Next retransmission in %s",
483                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
484                                          client->retransmit_time, 0));
485
486         r = sd_event_add_time(client->event, &client->timeout_resend,
487                               clock_boottime_or_monotonic(),
488                               time_now + client->retransmit_time,
489                               10 * USEC_PER_MSEC, client_timeout_resend,
490                               client);
491         if (r < 0)
492                 goto error;
493
494         r = sd_event_source_set_priority(client->timeout_resend,
495                                          client->event_priority);
496         if (r < 0)
497                 goto error;
498
499         r = sd_event_source_set_name(client->timeout_resend,
500                                      "dhcp6-resend-timer");
501         if (r < 0)
502                 goto error;
503
504         if (max_retransmit_duration && !client->timeout_resend_expire) {
505
506                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
507                                  max_retransmit_duration / USEC_PER_SEC);
508
509                 r = sd_event_add_time(client->event,
510                                       &client->timeout_resend_expire,
511                                       clock_boottime_or_monotonic(),
512                                       time_now + max_retransmit_duration,
513                                       USEC_PER_SEC,
514                                       client_timeout_resend_expire, client);
515                 if (r < 0)
516                         goto error;
517
518                 r = sd_event_source_set_priority(client->timeout_resend_expire,
519                                                  client->event_priority);
520                 if (r < 0)
521                         goto error;
522
523                 r = sd_event_source_set_name(client->timeout_resend_expire,
524                                              "dhcp6-resend-expire-timer");
525                 if (r < 0)
526                         goto error;
527         }
528
529 error:
530         if (r < 0)
531                 client_stop(client, r);
532
533         return 0;
534 }
535
536 static int client_ensure_iaid(sd_dhcp6_client *client) {
537         /* name is a pointer to memory in the udev_device struct, so must
538            have the same scope */
539         _cleanup_udev_device_unref_ struct udev_device *device = NULL;
540         const char *name = NULL;
541         uint64_t id;
542
543         assert(client);
544
545         if (client->ia_na.id)
546                 return 0;
547
548         if (detect_container(NULL) <= 0) {
549                 /* not in a container, udev will be around */
550                 _cleanup_udev_unref_ struct udev *udev;
551                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
552
553                 udev = udev_new();
554                 if (!udev)
555                         return -ENOMEM;
556
557                 sprintf(ifindex_str, "n%d", client->index);
558                 device = udev_device_new_from_device_id(udev, ifindex_str);
559                 if (!device)
560                         return -errno;
561
562                 if (udev_device_get_is_initialized(device) <= 0)
563                         /* not yet ready */
564                         return -EBUSY;
565
566                 name = net_get_name(device);
567         }
568
569         if (name)
570                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
571         else
572                 /* fall back to mac address if no predictable name available */
573                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
574                           HASH_KEY.bytes);
575
576         /* fold into 32 bits */
577         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
578
579         return 0;
580 }
581
582 static int client_parse_message(sd_dhcp6_client *client,
583                                 DHCP6Message *message, size_t len,
584                                 sd_dhcp6_lease *lease) {
585         int r;
586         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
587         uint16_t optcode, status;
588         size_t optlen, id_len;
589         bool clientid = false;
590         be32_t iaid_lease;
591
592         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
593                                        &optval)) >= 0) {
594                 switch (optcode) {
595                 case DHCP6_OPTION_CLIENTID:
596                         if (clientid) {
597                                 log_dhcp6_client(client, "%s contains multiple clientids",
598                                                  dhcp6_message_type_to_string(message->type));
599                                 return -EINVAL;
600                         }
601
602                         if (optlen != sizeof(client->duid) ||
603                             memcmp(&client->duid, optval, optlen) != 0) {
604                                 log_dhcp6_client(client, "%s DUID does not match",
605                                                  dhcp6_message_type_to_string(message->type));
606
607                                 return -EINVAL;
608                         }
609                         clientid = true;
610
611                         break;
612
613                 case DHCP6_OPTION_SERVERID:
614                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
615                         if (r >= 0 && id) {
616                                 log_dhcp6_client(client, "%s contains multiple serverids",
617                                                  dhcp6_message_type_to_string(message->type));
618                                 return -EINVAL;
619                         }
620
621                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
622                         if (r < 0)
623                                 return r;
624
625                         break;
626
627                 case DHCP6_OPTION_PREFERENCE:
628                         if (optlen != 1)
629                                 return -EINVAL;
630
631                         r = dhcp6_lease_set_preference(lease, *optval);
632                         if (r < 0)
633                                 return r;
634
635                         break;
636
637                 case DHCP6_OPTION_STATUS_CODE:
638                         if (optlen < 2)
639                                 return -EINVAL;
640
641                         status = optval[0] << 8 | optval[1];
642                         if (status) {
643                                 log_dhcp6_client(client, "%s Status %s",
644                                                  dhcp6_message_type_to_string(message->type),
645                                                  dhcp6_message_status_to_string(status));
646                                 return -EINVAL;
647                         }
648
649                         break;
650
651                 case DHCP6_OPTION_IA_NA:
652                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
653                                                   &lease->ia);
654                         if (r < 0 && r != -ENOMSG)
655                                 return r;
656
657                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
658                         if (r < 0)
659                                 return r;
660
661                         if (client->ia_na.id != iaid_lease) {
662                                 log_dhcp6_client(client, "%s has wrong IAID",
663                                                  dhcp6_message_type_to_string(message->type));
664                                 return -EINVAL;
665                         }
666
667                         break;
668
669                 case DHCP6_OPTION_RAPID_COMMIT:
670                         r = dhcp6_lease_set_rapid_commit(lease);
671                         if (r < 0)
672                                 return r;
673
674                         break;
675                 }
676         }
677
678         if ((r < 0 && r != -ENOMSG) || !clientid) {
679                 log_dhcp6_client(client, "%s has incomplete options",
680                                  dhcp6_message_type_to_string(message->type));
681                 return -EINVAL;
682         }
683
684         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
685         if (r < 0)
686                 log_dhcp6_client(client, "%s has no server id",
687                                  dhcp6_message_type_to_string(message->type));
688
689         return r;
690 }
691
692 static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply,
693                                 size_t len)
694 {
695         int r;
696         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
697         bool rapid_commit;
698
699         if (reply->type != DHCP6_REPLY)
700                 return 0;
701
702         r = dhcp6_lease_new(&lease);
703         if (r < 0)
704                 return -ENOMEM;
705
706         r = client_parse_message(client, reply, len, lease);
707         if (r < 0)
708                 return r;
709
710         if (client->state == DHCP6_STATE_SOLICITATION) {
711                 r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
712                 if (r < 0)
713                         return r;
714
715                 if (!rapid_commit)
716                         return 0;
717         }
718
719         if (client->lease)
720                 dhcp6_lease_clear_timers(&client->lease->ia);
721
722         client->lease = sd_dhcp6_lease_unref(client->lease);
723         client->lease = lease;
724         lease = NULL;
725
726         return DHCP6_STATE_BOUND;
727 }
728
729 static int client_receive_advertise(sd_dhcp6_client *client,
730                                     DHCP6Message *advertise, size_t len) {
731         int r;
732         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
733         uint8_t pref_advertise = 0, pref_lease = 0;
734
735         if (advertise->type != DHCP6_ADVERTISE)
736                 return 0;
737
738         r = dhcp6_lease_new(&lease);
739         if (r < 0)
740                 return r;
741
742         r = client_parse_message(client, advertise, len, lease);
743         if (r < 0)
744                 return r;
745
746         r = dhcp6_lease_get_preference(lease, &pref_advertise);
747         if (r < 0)
748                 return r;
749
750         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
751         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
752                 sd_dhcp6_lease_unref(client->lease);
753                 client->lease = lease;
754                 lease = NULL;
755                 r = 0;
756         }
757
758         if (pref_advertise == 255 || client->retransmit_count > 1)
759                 r = DHCP6_STATE_REQUEST;
760
761         return r;
762 }
763
764 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
765                                   void *userdata) {
766         sd_dhcp6_client *client = userdata;
767         DHCP6_CLIENT_DONT_DESTROY(client);
768         _cleanup_free_ DHCP6Message *message;
769         int r, buflen, len;
770
771         assert(s);
772         assert(client);
773         assert(client->event);
774
775         r = ioctl(fd, FIONREAD, &buflen);
776         if (r < 0 || buflen <= 0)
777                 buflen = DHCP6_MIN_OPTIONS_SIZE;
778
779         message = malloc0(buflen);
780         if (!message)
781                 return -ENOMEM;
782
783         len = read(fd, message, buflen);
784         if ((size_t)len < sizeof(DHCP6Message)) {
785                 log_dhcp6_client(client, "could not receive message from UDP socket: %m");
786                 return 0;
787         }
788
789         switch(message->type) {
790         case DHCP6_SOLICIT:
791         case DHCP6_REQUEST:
792         case DHCP6_CONFIRM:
793         case DHCP6_RENEW:
794         case DHCP6_REBIND:
795         case DHCP6_RELEASE:
796         case DHCP6_DECLINE:
797         case DHCP6_INFORMATION_REQUEST:
798         case DHCP6_RELAY_FORW:
799         case DHCP6_RELAY_REPL:
800                 return 0;
801
802         case DHCP6_ADVERTISE:
803         case DHCP6_REPLY:
804         case DHCP6_RECONFIGURE:
805                 break;
806
807         default:
808                 log_dhcp6_client(client, "unknown message type %d",
809                                  message->type);
810                 return 0;
811         }
812
813         if (client->transaction_id != (message->transaction_id &
814                                        htobe32(0x00ffffff)))
815                 return 0;
816
817         switch (client->state) {
818         case DHCP6_STATE_SOLICITATION:
819                 r = client_receive_advertise(client, message, len);
820
821                 if (r == DHCP6_STATE_REQUEST) {
822                         client_start(client, r);
823
824                         break;
825                 }
826
827                 /* fall through for Soliciation Rapid Commit option check */
828         case DHCP6_STATE_REQUEST:
829         case DHCP6_STATE_RENEW:
830         case DHCP6_STATE_REBIND:
831
832                 r = client_receive_reply(client, message, len);
833                 if (r < 0)
834                         return 0;
835
836                 if (r == DHCP6_STATE_BOUND) {
837
838                         r = client_start(client, DHCP6_STATE_BOUND);
839                         if (r < 0) {
840                                 client_stop(client, r);
841                                 return 0;
842                         }
843
844                         client_notify(client, DHCP6_EVENT_IP_ACQUIRE);
845                 }
846
847                 break;
848
849         case DHCP6_STATE_BOUND:
850
851                 break;
852
853         case DHCP6_STATE_STOPPED:
854                 return 0;
855         }
856
857         if (r >= 0) {
858                 log_dhcp6_client(client, "Recv %s",
859                                  dhcp6_message_type_to_string(message->type));
860         }
861
862         return 0;
863 }
864
865 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
866 {
867         int r;
868         usec_t timeout, time_now;
869         char time_string[FORMAT_TIMESPAN_MAX];
870
871         assert_return(client, -EINVAL);
872         assert_return(client->event, -EINVAL);
873         assert_return(client->index > 0, -EINVAL);
874         assert_return(client->state != state, -EINVAL);
875
876         client->timeout_resend_expire =
877                 sd_event_source_unref(client->timeout_resend_expire);
878         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
879         client->retransmit_time = 0;
880         client->retransmit_count = 0;
881
882         switch (state) {
883         case DHCP6_STATE_STOPPED:
884         case DHCP6_STATE_SOLICITATION:
885
886                 r = client_ensure_iaid(client);
887                 if (r < 0)
888                         return r;
889
890                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
891                 if (r < 0)
892                         return r;
893
894                 client->fd = r;
895
896                 r = sd_event_add_io(client->event, &client->receive_message,
897                                     client->fd, EPOLLIN, client_receive_message,
898                                     client);
899                 if (r < 0)
900                         return r;
901
902                 r = sd_event_source_set_priority(client->receive_message,
903                                                  client->event_priority);
904                 if (r < 0)
905                         return r;
906
907                 r = sd_event_source_set_name(client->receive_message,
908                                              "dhcp6-receive-message");
909                 if (r < 0)
910                         return r;
911
912                 client->state = DHCP6_STATE_SOLICITATION;
913
914                 break;
915
916         case DHCP6_STATE_REQUEST:
917         case DHCP6_STATE_RENEW:
918         case DHCP6_STATE_REBIND:
919
920                 client->state = state;
921
922                 break;
923
924         case DHCP6_STATE_BOUND:
925
926                 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
927                 if (r < 0)
928                         return r;
929
930                 if (client->lease->ia.lifetime_t1 == 0xffffffff ||
931                     client->lease->ia.lifetime_t2 == 0xffffffff) {
932
933                         log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x",
934                                          be32toh(client->lease->ia.lifetime_t1),
935                                          be32toh(client->lease->ia.lifetime_t2));
936
937                         return 0;
938                 }
939
940                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC);
941
942                 log_dhcp6_client(client, "T1 expires in %s",
943                                  format_timespan(time_string,
944                                                  FORMAT_TIMESPAN_MAX,
945                                                  timeout, 0));
946
947                 r = sd_event_add_time(client->event,
948                                       &client->lease->ia.timeout_t1,
949                                       clock_boottime_or_monotonic(), time_now + timeout,
950                                       10 * USEC_PER_SEC, client_timeout_t1,
951                                       client);
952                 if (r < 0)
953                         return r;
954
955                 r = sd_event_source_set_priority(client->lease->ia.timeout_t1,
956                                                  client->event_priority);
957                 if (r < 0)
958                         return r;
959
960                 r = sd_event_source_set_name(client->lease->ia.timeout_t1,
961                                              "dhcp6-t1-timeout");
962                 if (r < 0)
963                         return r;
964
965                 timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC);
966
967                 log_dhcp6_client(client, "T2 expires in %s",
968                                  format_timespan(time_string,
969                                                  FORMAT_TIMESPAN_MAX,
970                                                  timeout, 0));
971
972                 r = sd_event_add_time(client->event,
973                                       &client->lease->ia.timeout_t2,
974                                       clock_boottime_or_monotonic(), time_now + timeout,
975                                       10 * USEC_PER_SEC, client_timeout_t2,
976                                       client);
977                 if (r < 0)
978                         return r;
979
980                 r = sd_event_source_set_priority(client->lease->ia.timeout_t2,
981                                                  client->event_priority);
982                 if (r < 0)
983                         return r;
984
985                 r = sd_event_source_set_name(client->lease->ia.timeout_t2,
986                                              "dhcp6-t2-timeout");
987                 if (r < 0)
988                         return r;
989
990                 client->state = state;
991
992                 return 0;
993         }
994
995         client->transaction_id = random_u32() & htobe32(0x00ffffff);
996
997         r = sd_event_add_time(client->event, &client->timeout_resend,
998                               clock_boottime_or_monotonic(), 0, 0, client_timeout_resend,
999                               client);
1000         if (r < 0)
1001                 return r;
1002
1003         r = sd_event_source_set_priority(client->timeout_resend,
1004                                          client->event_priority);
1005         if (r < 0)
1006                 return r;
1007
1008         r = sd_event_source_set_name(client->timeout_resend,
1009                                      "dhcp6-resend-timeout");
1010         if (r < 0)
1011                 return r;
1012
1013         return 0;
1014 }
1015
1016 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
1017 {
1018         client_stop(client, DHCP6_EVENT_STOP);
1019
1020         return 0;
1021 }
1022
1023 int sd_dhcp6_client_start(sd_dhcp6_client *client)
1024 {
1025         int r = 0;
1026
1027         assert_return(client, -EINVAL);
1028         assert_return(client->event, -EINVAL);
1029         assert_return(client->index > 0, -EINVAL);
1030
1031         r = client_reset(client);
1032         if (r < 0)
1033                 return r;
1034
1035         return client_start(client, DHCP6_STATE_SOLICITATION);
1036 }
1037
1038 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
1039                                  int priority)
1040 {
1041         int r;
1042
1043         assert_return(client, -EINVAL);
1044         assert_return(!client->event, -EBUSY);
1045
1046         if (event)
1047                 client->event = sd_event_ref(event);
1048         else {
1049                 r = sd_event_default(&client->event);
1050                 if (r < 0)
1051                         return 0;
1052         }
1053
1054         client->event_priority = priority;
1055
1056         return 0;
1057 }
1058
1059 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1060         assert_return(client, -EINVAL);
1061
1062         client->event = sd_event_unref(client->event);
1063
1064         return 0;
1065 }
1066
1067 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1068         if (!client)
1069                 return NULL;
1070
1071         return client->event;
1072 }
1073
1074 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
1075         if (client)
1076                 assert_se(REFCNT_INC(client->n_ref) >= 2);
1077
1078         return client;
1079 }
1080
1081 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
1082         if (client && REFCNT_DEC(client->n_ref) <= 0) {
1083                 client_reset(client);
1084
1085                 sd_dhcp6_client_detach_event(client);
1086
1087                 free(client->req_opts);
1088                 free(client);
1089
1090                 return NULL;
1091         }
1092
1093         return client;
1094 }
1095
1096 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
1097 {
1098         _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL;
1099         sd_id128_t machine_id;
1100         int r;
1101         size_t t;
1102
1103         assert_return(ret, -EINVAL);
1104
1105         client = new0(sd_dhcp6_client, 1);
1106         if (!client)
1107                 return -ENOMEM;
1108
1109         client->n_ref = REFCNT_INIT;
1110
1111         client->ia_na.type = DHCP6_OPTION_IA_NA;
1112
1113         client->index = -1;
1114
1115         client->fd = -1;
1116
1117         /* initialize DUID */
1118         client->duid.type = htobe16(DHCP6_DUID_EN);
1119         client->duid.pen = htobe32(SYSTEMD_PEN);
1120
1121         r = sd_id128_get_machine(&machine_id);
1122         if (r < 0)
1123                 return r;
1124
1125         /* a bit of snake-oil perhaps, but no need to expose the machine-id
1126            directly */
1127         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
1128                   HASH_KEY.bytes);
1129
1130         client->req_opts_len = ELEMENTSOF(default_req_opts);
1131
1132         client->req_opts = new0(be16_t, client->req_opts_len);
1133         if (!client->req_opts)
1134                 return -ENOMEM;
1135
1136         for (t = 0; t < client->req_opts_len; t++)
1137                 client->req_opts[t] = htobe16(default_req_opts[t]);
1138
1139         *ret = client;
1140         client = NULL;
1141
1142         return 0;
1143 }