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