]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-dhcp6-client.c
sd-dhcp6-client: Add Request message sending
[thirdparty/systemd.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 }