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