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