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