chiark / gitweb /
55dd65c9eb9d6af4a051c68400507b7acb2b8c2e
[elogind.git] / src / libsystemd-network / sd-dhcp6-client.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 Intel Corporation. All rights reserved.
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25
26 #include "udev.h"
27 #include "udev-util.h"
28 #include "virt.h"
29 #include "siphash24.h"
30 #include "util.h"
31 #include "refcnt.h"
32
33 #include "network-internal.h"
34 #include "sd-dhcp6-client.h"
35 #include "dhcp6-protocol.h"
36 #include "dhcp6-internal.h"
37 #include "dhcp6-lease-internal.h"
38
39 #define SYSTEMD_PEN 43793
40 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
41
42 struct sd_dhcp6_client {
43         RefCount n_ref;
44
45         enum DHCP6State state;
46         sd_event *event;
47         int event_priority;
48         int index;
49         struct ether_addr mac_addr;
50         DHCP6IA ia_na;
51         be32_t transaction_id;
52         struct sd_dhcp6_lease *lease;
53         int fd;
54         sd_event_source *receive_message;
55         usec_t retransmit_time;
56         uint8_t retransmit_count;
57         sd_event_source *timeout_resend;
58         sd_event_source *timeout_resend_expire;
59         sd_dhcp6_client_cb_t cb;
60         void *userdata;
61
62         struct duid_en {
63                 uint16_t type; /* DHCP6_DUID_EN */
64                 uint32_t pen;
65                 uint8_t id[8];
66         } _packed_ duid;
67 };
68
69 const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
70         [DHCP6_SOLICIT] = "SOLICIT",
71         [DHCP6_ADVERTISE] = "ADVERTISE",
72         [DHCP6_REQUEST] = "REQUEST",
73         [DHCP6_CONFIRM] = "CONFIRM",
74         [DHCP6_RENEW] = "RENEW",
75         [DHCP6_REBIND] = "REBIND",
76         [DHCP6_REPLY] = "REPLY",
77         [DHCP6_RELEASE] = "RELEASE",
78         [DHCP6_DECLINE] = "DECLINE",
79         [DHCP6_RECONFIGURE] = "RECONFIGURE",
80         [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
81         [DHCP6_RELAY_FORW] = "RELAY-FORW",
82         [DHCP6_RELAY_REPL] = "RELAY-REPL",
83 };
84
85 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
86
87 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
88         [DHCP6_STATUS_SUCCESS] = "Success",
89         [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
90         [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
91         [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
92         [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
93         [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
94 };
95
96 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
97
98 static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
99
100 int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
101                                  sd_dhcp6_client_cb_t cb, void *userdata)
102 {
103         assert_return(client, -EINVAL);
104
105         client->cb = cb;
106         client->userdata = userdata;
107
108         return 0;
109 }
110
111 int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index)
112 {
113         assert_return(client, -EINVAL);
114         assert_return(interface_index >= -1, -EINVAL);
115
116         client->index = interface_index;
117
118         return 0;
119 }
120
121 int sd_dhcp6_client_set_mac(sd_dhcp6_client *client,
122                             const struct ether_addr *mac_addr)
123 {
124         assert_return(client, -EINVAL);
125
126         if (mac_addr)
127                 memcpy(&client->mac_addr, mac_addr, sizeof(client->mac_addr));
128         else
129                 memset(&client->mac_addr, 0x00, sizeof(client->mac_addr));
130
131         return 0;
132 }
133
134 int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
135         assert_return(client, -EINVAL);
136         assert_return(ret, -EINVAL);
137
138         if (!client->lease)
139                 return -ENOMSG;
140
141         *ret = sd_dhcp6_lease_ref(client->lease);
142
143         return 0;
144 }
145
146 static sd_dhcp6_client *client_notify(sd_dhcp6_client *client, int event) {
147         if (client->cb) {
148                 client = sd_dhcp6_client_ref(client);
149                 client->cb(client, event, client->userdata);
150                 client = sd_dhcp6_client_unref(client);
151         }
152
153         return client;
154 }
155
156 static int client_initialize(sd_dhcp6_client *client)
157 {
158         assert_return(client, -EINVAL);
159
160         client->receive_message =
161                 sd_event_source_unref(client->receive_message);
162
163         if (client->fd > 0)
164                 client->fd = safe_close(client->fd);
165
166         client->transaction_id = 0;
167
168         client->ia_na.timeout_t1 =
169                 sd_event_source_unref(client->ia_na.timeout_t1);
170         client->ia_na.timeout_t2 =
171                 sd_event_source_unref(client->ia_na.timeout_t2);
172
173         client->retransmit_time = 0;
174         client->retransmit_count = 0;
175         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
176         client->timeout_resend_expire =
177                 sd_event_source_unref(client->timeout_resend_expire);
178
179         client->state = DHCP6_STATE_STOPPED;
180
181         return 0;
182 }
183
184 static sd_dhcp6_client *client_stop(sd_dhcp6_client *client, int error) {
185         assert_return(client, NULL);
186
187         client = client_notify(client, error);
188         if (client)
189                 client_initialize(client);
190
191         return client;
192 }
193
194 static int client_send_message(sd_dhcp6_client *client) {
195         _cleanup_free_ DHCP6Message *message = NULL;
196         struct in6_addr all_servers =
197                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
198         size_t len, optlen = 512;
199         uint8_t *opt;
200         int r;
201
202         len = sizeof(DHCP6Message) + optlen;
203
204         message = malloc0(len);
205         if (!message)
206                 return -ENOMEM;
207
208         opt = (uint8_t *)(message + 1);
209
210         message->transaction_id = client->transaction_id;
211
212         switch(client->state) {
213         case DHCP6_STATE_SOLICITATION:
214                 message->type = DHCP6_SOLICIT;
215
216                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
217                 if (r < 0)
218                         return r;
219
220                 break;
221
222         case DHCP6_STATE_REQUEST:
223                 message->type = DHCP6_REQUEST;
224
225                 r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_SERVERID,
226                                         client->lease->serverid_len,
227                                         client->lease->serverid);
228                 if (r < 0)
229                         return r;
230
231                 r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
232                 if (r < 0)
233                         return r;
234
235                 break;
236
237         case DHCP6_STATE_STOPPED:
238         case DHCP6_STATE_RS:
239                 return -EINVAL;
240         }
241
242         r = dhcp6_option_append(&opt, &optlen, DHCP6_OPTION_CLIENTID,
243                                 sizeof(client->duid), &client->duid);
244         if (r < 0)
245                 return r;
246
247         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
248                                           len - optlen);
249         if (r < 0)
250                 return r;
251
252         log_dhcp6_client(client, "Sent %s",
253                          dhcp6_message_type_to_string(message->type));
254
255         return 0;
256 }
257
258 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
259                                         void *userdata) {
260         sd_dhcp6_client *client = userdata;
261
262         assert(s);
263         assert(client);
264         assert(client->event);
265
266         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
267
268         return 0;
269 }
270
271 static usec_t client_timeout_compute_random(usec_t val) {
272         return val - val / 10 +
273                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
274 }
275
276 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
277                                  void *userdata) {
278         int r = 0;
279         sd_dhcp6_client *client = userdata;
280         usec_t time_now, init_retransmit_time, max_retransmit_time;
281         usec_t max_retransmit_duration;
282         uint8_t max_retransmit_count;
283         char time_string[FORMAT_TIMESPAN_MAX];
284
285         assert(s);
286         assert(client);
287         assert(client->event);
288
289         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
290
291         switch (client->state) {
292         case DHCP6_STATE_SOLICITATION:
293
294                 if (client->retransmit_count && client->lease) {
295                         client_start(client, DHCP6_STATE_REQUEST);
296                         return 0;
297                 }
298
299                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
300                 max_retransmit_time = DHCP6_SOL_MAX_RT;
301                 max_retransmit_count = 0;
302                 max_retransmit_duration = 0;
303
304                 break;
305
306         case DHCP6_STATE_REQUEST:
307                 init_retransmit_time = DHCP6_REQ_TIMEOUT;
308                 max_retransmit_time = DHCP6_REQ_MAX_RT;
309                 max_retransmit_count = DHCP6_REQ_MAX_RC;
310                 max_retransmit_duration = 0;
311
312                 break;
313
314         case DHCP6_STATE_STOPPED:
315         case DHCP6_STATE_RS:
316                 return 0;
317         }
318
319         if (max_retransmit_count &&
320             client->retransmit_count >= max_retransmit_count) {
321                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
322                 return 0;
323         }
324
325         r = client_send_message(client);
326         if (r >= 0)
327                 client->retransmit_count++;
328
329
330         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
331         if (r < 0)
332                 goto error;
333
334         if (!client->retransmit_time) {
335                 client->retransmit_time =
336                         client_timeout_compute_random(init_retransmit_time);
337
338                 if (client->state == DHCP6_STATE_SOLICITATION)
339                         client->retransmit_time += init_retransmit_time / 10;
340
341         } else {
342                 if (max_retransmit_time &&
343                     client->retransmit_time > max_retransmit_time / 2)
344                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
345                 else
346                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
347         }
348
349         log_dhcp6_client(client, "Next retransmission in %s",
350                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
351                                          client->retransmit_time, 0));
352
353         r = sd_event_add_time(client->event, &client->timeout_resend,
354                               CLOCK_MONOTONIC,
355                               time_now + client->retransmit_time,
356                               10 * USEC_PER_MSEC, client_timeout_resend,
357                               client);
358         if (r < 0)
359                 goto error;
360
361         r = sd_event_source_set_priority(client->timeout_resend,
362                                          client->event_priority);
363         if (r < 0)
364                 goto error;
365
366         if (max_retransmit_duration && !client->timeout_resend_expire) {
367
368                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
369                                  max_retransmit_duration / USEC_PER_SEC);
370
371                 r = sd_event_add_time(client->event,
372                                       &client->timeout_resend_expire,
373                                       CLOCK_MONOTONIC,
374                                       time_now + max_retransmit_duration,
375                                       USEC_PER_SEC,
376                                       client_timeout_resend_expire, client);
377                 if (r < 0)
378                         goto error;
379
380                 r = sd_event_source_set_priority(client->timeout_resend_expire,
381                                                  client->event_priority);
382                 if (r < 0)
383                         goto error;
384         }
385
386 error:
387         if (r < 0)
388                 client_stop(client, r);
389
390         return 0;
391 }
392
393 static int client_ensure_iaid(sd_dhcp6_client *client) {
394         const char *name = NULL;
395         uint64_t id;
396
397         assert(client);
398
399         if (client->ia_na.id)
400                 return 0;
401
402         if (detect_container(NULL) <= 0) {
403                 /* not in a container, udev will be around */
404                 _cleanup_udev_unref_ struct udev *udev;
405                 _cleanup_udev_device_unref_ struct udev_device *device;
406                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
407
408                 udev = udev_new();
409                 if (!udev)
410                         return -ENOMEM;
411
412                 sprintf(ifindex_str, "n%d", client->index);
413                 device = udev_device_new_from_device_id(udev, ifindex_str);
414                 if (!device)
415                         return -errno;
416
417                 if (udev_device_get_is_initialized(device) <= 0)
418                         /* not yet ready */
419                         return -EBUSY;
420
421                 name = net_get_name(device);
422         }
423
424         if (name)
425                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
426         else
427                 /* fall back to mac address if no predictable name available */
428                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
429                           HASH_KEY.bytes);
430
431         /* fold into 32 bits */
432         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
433
434         return 0;
435 }
436
437 static int client_parse_message(sd_dhcp6_client *client,
438                                 DHCP6Message *message, size_t len,
439                                 sd_dhcp6_lease *lease) {
440         int r;
441         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
442         uint16_t optcode, status;
443         size_t optlen, id_len;
444         bool clientid = false;
445         be32_t iaid_lease;
446
447         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
448                                        &optval)) >= 0) {
449                 switch (optcode) {
450                 case DHCP6_OPTION_CLIENTID:
451                         if (clientid) {
452                                 log_dhcp6_client(client, "%s contains multiple clientids",
453                                                  dhcp6_message_type_to_string(message->type));
454                                 return -EINVAL;
455                         }
456
457                         if (optlen != sizeof(client->duid) ||
458                             memcmp(&client->duid, optval, optlen) != 0) {
459                                 log_dhcp6_client(client, "%s DUID does not match",
460                                                  dhcp6_message_type_to_string(message->type));
461
462                                 return -EINVAL;
463                         }
464                         clientid = true;
465
466                         break;
467
468                 case DHCP6_OPTION_SERVERID:
469                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
470                         if (r >= 0 && id) {
471                                 log_dhcp6_client(client, "%s contains multiple serverids",
472                                                  dhcp6_message_type_to_string(message->type));
473                                 return -EINVAL;
474                         }
475
476                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
477                         if (r < 0)
478                                 return r;
479
480                         break;
481
482                 case DHCP6_OPTION_PREFERENCE:
483                         if (optlen != 1)
484                                 return -EINVAL;
485
486                         r = dhcp6_lease_set_preference(lease, *optval);
487                         if (r < 0)
488                                 return r;
489
490                         break;
491
492                 case DHCP6_OPTION_STATUS_CODE:
493                         if (optlen < 2)
494                                 return -EINVAL;
495
496                         status = optval[0] << 8 | optval[1];
497                         if (status) {
498                                 log_dhcp6_client(client, "%s Status %s",
499                                                  dhcp6_message_type_to_string(message->type),
500                                                  dhcp6_message_status_to_string(status));
501                                 return -EINVAL;
502                         }
503
504                         break;
505
506                 case DHCP6_OPTION_IA_NA:
507                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
508                                                   &lease->ia);
509                         if (r < 0 && r != -ENOMSG)
510                                 return r;
511
512                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
513                         if (r < 0)
514                                 return r;
515
516                         if (client->ia_na.id != iaid_lease) {
517                                 log_dhcp6_client(client, "%s has wrong IAID",
518                                                  dhcp6_message_type_to_string(message->type));
519                                 return -EINVAL;
520                         }
521
522                         break;
523                 }
524         }
525
526         if ((r < 0 && r != -ENOMSG) || !clientid) {
527                 log_dhcp6_client(client, "%s has incomplete options",
528                                  dhcp6_message_type_to_string(message->type));
529                 return -EINVAL;
530         }
531
532         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
533         if (r < 0)
534                 log_dhcp6_client(client, "%s has no server id",
535                                  dhcp6_message_type_to_string(message->type));
536
537         return r;
538 }
539
540 static int client_receive_advertise(sd_dhcp6_client *client,
541                                     DHCP6Message *advertise, size_t len) {
542         int r;
543         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
544         uint8_t pref_advertise = 0, pref_lease = 0;
545
546         if (advertise->type != DHCP6_ADVERTISE)
547                 return -EINVAL;
548
549         r = dhcp6_lease_new(&lease);
550         if (r < 0)
551                 return r;
552
553         r = client_parse_message(client, advertise, len, lease);
554         if (r < 0)
555                 return r;
556
557         r = dhcp6_lease_get_preference(lease, &pref_advertise);
558         if (r < 0)
559                 return r;
560
561         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
562         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
563                 sd_dhcp6_lease_unref(client->lease);
564                 client->lease = lease;
565                 lease = NULL;
566                 r = 0;
567         }
568
569         if (pref_advertise == 255 || client->retransmit_count > 1)
570                 r = DHCP6_STATE_REQUEST;
571
572         return r;
573 }
574
575 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
576                                   void *userdata) {
577         sd_dhcp6_client *client = userdata;
578         _cleanup_free_ DHCP6Message *message;
579         int r, buflen, len;
580
581         assert(s);
582         assert(client);
583         assert(client->event);
584
585         r = ioctl(fd, FIONREAD, &buflen);
586         if (r < 0 || buflen <= 0)
587                 buflen = DHCP6_MIN_OPTIONS_SIZE;
588
589         message = malloc0(buflen);
590         if (!message)
591                 return -ENOMEM;
592
593         len = read(fd, message, buflen);
594         if ((size_t)len < sizeof(DHCP6Message)) {
595                 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
596                 return 0;
597         }
598
599         switch(message->type) {
600         case DHCP6_SOLICIT:
601         case DHCP6_REQUEST:
602         case DHCP6_CONFIRM:
603         case DHCP6_RENEW:
604         case DHCP6_REBIND:
605         case DHCP6_RELEASE:
606         case DHCP6_DECLINE:
607         case DHCP6_INFORMATION_REQUEST:
608         case DHCP6_RELAY_FORW:
609         case DHCP6_RELAY_REPL:
610                 return 0;
611
612         case DHCP6_ADVERTISE:
613         case DHCP6_REPLY:
614         case DHCP6_RECONFIGURE:
615                 break;
616
617         default:
618                 log_dhcp6_client(client, "unknown message type %d",
619                                  message->type);
620                 return 0;
621         }
622
623         if (client->transaction_id != (message->transaction_id &
624                                        htobe32(0x00ffffff)))
625                 return 0;
626
627         switch (client->state) {
628         case DHCP6_STATE_SOLICITATION:
629                 r = client_receive_advertise(client, message, len);
630
631                 if (r == DHCP6_STATE_REQUEST)
632                         client_start(client, r);
633
634                 break;
635
636         case DHCP6_STATE_REQUEST:
637         case DHCP6_STATE_STOPPED:
638         case DHCP6_STATE_RS:
639                 return 0;
640         }
641
642         if (r >= 0) {
643                 log_dhcp6_client(client, "Recv %s",
644                                  dhcp6_message_type_to_string(message->type));
645         }
646
647         return 0;
648 }
649
650 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
651 {
652         int r;
653
654         assert_return(client, -EINVAL);
655         assert_return(client->event, -EINVAL);
656         assert_return(client->index > 0, -EINVAL);
657         assert_return(client->state != state, -EINVAL);
658
659         client->timeout_resend_expire =
660                 sd_event_source_unref(client->timeout_resend_expire);
661         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
662         client->retransmit_time = 0;
663         client->retransmit_count = 0;
664
665         switch (state) {
666         case DHCP6_STATE_STOPPED:
667         case DHCP6_STATE_RS:
668         case DHCP6_STATE_SOLICITATION:
669
670                 r = client_ensure_iaid(client);
671                 if (r < 0)
672                         return r;
673
674                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
675                 if (r < 0)
676                         return r;
677
678                 client->fd = r;
679
680                 r = sd_event_add_io(client->event, &client->receive_message,
681                                     client->fd, EPOLLIN, client_receive_message,
682                                     client);
683                 if (r < 0)
684                         return r;
685
686                 r = sd_event_source_set_priority(client->receive_message,
687                                                  client->event_priority);
688                 if (r < 0)
689                         return r;
690
691                 client->state = DHCP6_STATE_SOLICITATION;
692
693                 break;
694
695         case DHCP6_STATE_REQUEST:
696                 client->state = state;
697
698                 break;
699         }
700
701         client->transaction_id = random_u32() & htobe32(0x00ffffff);
702
703         r = sd_event_add_time(client->event, &client->timeout_resend,
704                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
705                               client);
706         if (r < 0)
707                 return r;
708
709         r = sd_event_source_set_priority(client->timeout_resend,
710                                          client->event_priority);
711         if (r < 0)
712                 return r;
713
714         return 0;
715 }
716
717 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
718 {
719         client_stop(client, DHCP6_EVENT_STOP);
720
721         return 0;
722 }
723
724 int sd_dhcp6_client_start(sd_dhcp6_client *client)
725 {
726         int r = 0;
727
728         assert_return(client, -EINVAL);
729         assert_return(client->event, -EINVAL);
730         assert_return(client->index > 0, -EINVAL);
731
732         r = client_initialize(client);
733         if (r < 0)
734                 return r;
735
736         return client_start(client, DHCP6_STATE_SOLICITATION);
737 }
738
739 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
740                                  int priority)
741 {
742         int r;
743
744         assert_return(client, -EINVAL);
745         assert_return(!client->event, -EBUSY);
746
747         if (event)
748                 client->event = sd_event_ref(event);
749         else {
750                 r = sd_event_default(&client->event);
751                 if (r < 0)
752                         return 0;
753         }
754
755         client->event_priority = priority;
756
757         return 0;
758 }
759
760 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
761         assert_return(client, -EINVAL);
762
763         client->event = sd_event_unref(client->event);
764
765         return 0;
766 }
767
768 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
769         if (!client)
770                 return NULL;
771
772         return client->event;
773 }
774
775 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
776         if (client)
777                 assert_se(REFCNT_INC(client->n_ref) >= 2);
778
779         return client;
780 }
781
782 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
783         if (client && REFCNT_DEC(client->n_ref) <= 0) {
784                 client_initialize(client);
785
786                 sd_dhcp6_client_detach_event(client);
787
788                 free(client);
789
790                 return NULL;
791         }
792
793         return client;
794 }
795
796 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
797 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
798
799 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
800 {
801         _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
802         sd_id128_t machine_id;
803         int r;
804
805         assert_return(ret, -EINVAL);
806
807         client = new0(sd_dhcp6_client, 1);
808         if (!client)
809                 return -ENOMEM;
810
811         client->n_ref = REFCNT_INIT;
812
813         client->ia_na.type = DHCP6_OPTION_IA_NA;
814
815         client->index = -1;
816
817         /* initialize DUID */
818         client->duid.type = htobe16(DHCP6_DUID_EN);
819         client->duid.pen = htobe32(SYSTEMD_PEN);
820
821         r = sd_id128_get_machine(&machine_id);
822         if (r < 0)
823                 return r;
824
825         /* a bit of snake-oil perhaps, but no need to expose the machine-id
826            directly */
827         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
828                   HASH_KEY.bytes);
829
830         *ret = client;
831         client = NULL;
832
833         return 0;
834 }