chiark / gitweb /
sd-dhcp6-client: Update start function to take a state
[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(&opt, &optlen, DHCP6_OPTION_CLIENTID,
217                                         sizeof(client->duid), &client->duid);
218                 if (r < 0)
219                         return r;
220
221                 r = dhcp6_option_append_ia(&opt, &optlen, &client->ia_na);
222                 if (r < 0)
223                         return r;
224
225                 break;
226
227         case DHCP6_STATE_STOPPED:
228         case DHCP6_STATE_RS:
229                 return -EINVAL;
230         }
231
232         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
233                                           len - optlen);
234         if (r < 0)
235                 return r;
236
237         log_dhcp6_client(client, "Sent %s",
238                          dhcp6_message_type_to_string(message->type));
239
240         return 0;
241 }
242
243 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec,
244                                         void *userdata) {
245         sd_dhcp6_client *client = userdata;
246
247         assert(s);
248         assert(client);
249         assert(client->event);
250
251         client_stop(client, DHCP6_EVENT_RESEND_EXPIRE);
252
253         return 0;
254 }
255
256 static usec_t client_timeout_compute_random(usec_t val) {
257         return val - val / 10 +
258                 (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
259 }
260
261 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
262                                  void *userdata) {
263         int r = 0;
264         sd_dhcp6_client *client = userdata;
265         usec_t time_now, init_retransmit_time, max_retransmit_time;
266         usec_t max_retransmit_duration;
267         uint8_t max_retransmit_count;
268         char time_string[FORMAT_TIMESPAN_MAX];
269
270         assert(s);
271         assert(client);
272         assert(client->event);
273
274         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
275
276         switch (client->state) {
277         case DHCP6_STATE_SOLICITATION:
278                 init_retransmit_time = DHCP6_SOL_TIMEOUT;
279                 max_retransmit_time = DHCP6_SOL_MAX_RT;
280                 max_retransmit_count = 0;
281                 max_retransmit_duration = 0;
282
283                 break;
284
285         case DHCP6_STATE_STOPPED:
286         case DHCP6_STATE_RS:
287                 return 0;
288         }
289
290         if (max_retransmit_count &&
291             client->retransmit_count >= max_retransmit_count) {
292                 client_stop(client, DHCP6_EVENT_RETRANS_MAX);
293                 return 0;
294         }
295
296         r = client_send_message(client);
297         if (r >= 0)
298                 client->retransmit_count++;
299
300
301         r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
302         if (r < 0)
303                 goto error;
304
305         if (!client->retransmit_time) {
306                 client->retransmit_time =
307                         client_timeout_compute_random(init_retransmit_time);
308
309                 if (client->state == DHCP6_STATE_SOLICITATION)
310                         client->retransmit_time += init_retransmit_time / 10;
311
312         } else {
313                 if (max_retransmit_time &&
314                     client->retransmit_time > max_retransmit_time / 2)
315                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
316                 else
317                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
318         }
319
320         log_dhcp6_client(client, "Next retransmission in %s",
321                          format_timespan(time_string, FORMAT_TIMESPAN_MAX,
322                                          client->retransmit_time, 0));
323
324         r = sd_event_add_time(client->event, &client->timeout_resend,
325                               CLOCK_MONOTONIC,
326                               time_now + client->retransmit_time,
327                               10 * USEC_PER_MSEC, client_timeout_resend,
328                               client);
329         if (r < 0)
330                 goto error;
331
332         r = sd_event_source_set_priority(client->timeout_resend,
333                                          client->event_priority);
334         if (r < 0)
335                 goto error;
336
337         if (max_retransmit_duration && !client->timeout_resend_expire) {
338
339                 log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
340                                  max_retransmit_duration / USEC_PER_SEC);
341
342                 r = sd_event_add_time(client->event,
343                                       &client->timeout_resend_expire,
344                                       CLOCK_MONOTONIC,
345                                       time_now + max_retransmit_duration,
346                                       USEC_PER_SEC,
347                                       client_timeout_resend_expire, client);
348                 if (r < 0)
349                         goto error;
350
351                 r = sd_event_source_set_priority(client->timeout_resend_expire,
352                                                  client->event_priority);
353                 if (r < 0)
354                         goto error;
355         }
356
357 error:
358         if (r < 0)
359                 client_stop(client, r);
360
361         return 0;
362 }
363
364 static int client_ensure_iaid(sd_dhcp6_client *client) {
365         const char *name = NULL;
366         uint64_t id;
367
368         assert(client);
369
370         if (client->ia_na.id)
371                 return 0;
372
373         if (detect_container(NULL) <= 0) {
374                 /* not in a container, udev will be around */
375                 _cleanup_udev_unref_ struct udev *udev;
376                 _cleanup_udev_device_unref_ struct udev_device *device;
377                 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
378
379                 udev = udev_new();
380                 if (!udev)
381                         return -ENOMEM;
382
383                 sprintf(ifindex_str, "n%d", client->index);
384                 device = udev_device_new_from_device_id(udev, ifindex_str);
385                 if (!device)
386                         return -errno;
387
388                 if (udev_device_get_is_initialized(device) <= 0)
389                         /* not yet ready */
390                         return -EBUSY;
391
392                 name = net_get_name(device);
393         }
394
395         if (name)
396                 siphash24((uint8_t*)&id, name, strlen(name), HASH_KEY.bytes);
397         else
398                 /* fall back to mac address if no predictable name available */
399                 siphash24((uint8_t*)&id, &client->mac_addr, ETH_ALEN,
400                           HASH_KEY.bytes);
401
402         /* fold into 32 bits */
403         client->ia_na.id = (id & 0xffffffff) ^ (id >> 32);
404
405         return 0;
406 }
407
408 static int client_parse_message(sd_dhcp6_client *client,
409                                 DHCP6Message *message, size_t len,
410                                 sd_dhcp6_lease *lease) {
411         int r;
412         uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
413         uint16_t optcode, status;
414         size_t optlen, id_len;
415         bool clientid = false;
416         be32_t iaid_lease;
417
418         while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
419                                        &optval)) >= 0) {
420                 switch (optcode) {
421                 case DHCP6_OPTION_CLIENTID:
422                         if (clientid) {
423                                 log_dhcp6_client(client, "%s contains multiple clientids",
424                                                  dhcp6_message_type_to_string(message->type));
425                                 return -EINVAL;
426                         }
427
428                         if (optlen != sizeof(client->duid) ||
429                             memcmp(&client->duid, optval, optlen) != 0) {
430                                 log_dhcp6_client(client, "%s DUID does not match",
431                                                  dhcp6_message_type_to_string(message->type));
432
433                                 return -EINVAL;
434                         }
435                         clientid = true;
436
437                         break;
438
439                 case DHCP6_OPTION_SERVERID:
440                         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
441                         if (r >= 0 && id) {
442                                 log_dhcp6_client(client, "%s contains multiple serverids",
443                                                  dhcp6_message_type_to_string(message->type));
444                                 return -EINVAL;
445                         }
446
447                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
448                         if (r < 0)
449                                 return r;
450
451                         break;
452
453                 case DHCP6_OPTION_PREFERENCE:
454                         if (optlen != 1)
455                                 return -EINVAL;
456
457                         r = dhcp6_lease_set_preference(lease, *optval);
458                         if (r < 0)
459                                 return r;
460
461                         break;
462
463                 case DHCP6_OPTION_STATUS_CODE:
464                         if (optlen < 2)
465                                 return -EINVAL;
466
467                         status = optval[0] << 8 | optval[1];
468                         if (status) {
469                                 log_dhcp6_client(client, "%s Status %s",
470                                                  dhcp6_message_type_to_string(message->type),
471                                                  dhcp6_message_status_to_string(status));
472                                 return -EINVAL;
473                         }
474
475                         break;
476
477                 case DHCP6_OPTION_IA_NA:
478                         r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
479                                                   &lease->ia);
480                         if (r < 0 && r != -ENOMSG)
481                                 return r;
482
483                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
484                         if (r < 0)
485                                 return r;
486
487                         if (client->ia_na.id != iaid_lease) {
488                                 log_dhcp6_client(client, "%s has wrong IAID",
489                                                  dhcp6_message_type_to_string(message->type));
490                                 return -EINVAL;
491                         }
492
493                         break;
494                 }
495         }
496
497         if ((r < 0 && r != -ENOMSG) || !clientid) {
498                 log_dhcp6_client(client, "%s has incomplete options",
499                                  dhcp6_message_type_to_string(message->type));
500                 return -EINVAL;
501         }
502
503         r = dhcp6_lease_get_serverid(lease, &id, &id_len);
504         if (r < 0)
505                 log_dhcp6_client(client, "%s has no server id",
506                                  dhcp6_message_type_to_string(message->type));
507
508         return r;
509 }
510
511 static int client_receive_advertise(sd_dhcp6_client *client,
512                                     DHCP6Message *advertise, size_t len) {
513         int r;
514         _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
515         uint8_t pref_advertise = 0, pref_lease = 0;
516
517         if (advertise->type != DHCP6_ADVERTISE)
518                 return -EINVAL;
519
520         r = dhcp6_lease_new(&lease);
521         if (r < 0)
522                 return r;
523
524         r = client_parse_message(client, advertise, len, lease);
525         if (r < 0)
526                 return r;
527
528         r = dhcp6_lease_get_preference(lease, &pref_advertise);
529         if (r < 0)
530                 return r;
531
532         r = dhcp6_lease_get_preference(client->lease, &pref_lease);
533         if (!client->lease || r < 0 || pref_advertise > pref_lease) {
534                 sd_dhcp6_lease_unref(client->lease);
535                 client->lease = lease;
536                 lease = NULL;
537                 r = 0;
538         }
539
540         return r;
541 }
542
543 static int client_receive_message(sd_event_source *s, int fd, uint32_t revents,
544                                   void *userdata) {
545         sd_dhcp6_client *client = userdata;
546         _cleanup_free_ DHCP6Message *message;
547         int r, buflen, len;
548
549         assert(s);
550         assert(client);
551         assert(client->event);
552
553         r = ioctl(fd, FIONREAD, &buflen);
554         if (r < 0 || buflen <= 0)
555                 buflen = DHCP6_MIN_OPTIONS_SIZE;
556
557         message = malloc0(buflen);
558         if (!message)
559                 return -ENOMEM;
560
561         len = read(fd, message, buflen);
562         if ((size_t)len < sizeof(DHCP6Message)) {
563                 log_dhcp6_client(client, "could not receive message from UDP socket: %s", strerror(errno));
564                 return 0;
565         }
566
567         switch(message->type) {
568         case DHCP6_SOLICIT:
569         case DHCP6_REQUEST:
570         case DHCP6_CONFIRM:
571         case DHCP6_RENEW:
572         case DHCP6_REBIND:
573         case DHCP6_RELEASE:
574         case DHCP6_DECLINE:
575         case DHCP6_INFORMATION_REQUEST:
576         case DHCP6_RELAY_FORW:
577         case DHCP6_RELAY_REPL:
578                 return 0;
579
580         case DHCP6_ADVERTISE:
581         case DHCP6_REPLY:
582         case DHCP6_RECONFIGURE:
583                 break;
584
585         default:
586                 log_dhcp6_client(client, "unknown message type %d",
587                                  message->type);
588                 return 0;
589         }
590
591         if (client->transaction_id != (message->transaction_id &
592                                        htobe32(0x00ffffff)))
593                 return 0;
594
595         switch (client->state) {
596         case DHCP6_STATE_SOLICITATION:
597                 r = client_receive_advertise(client, message, len);
598
599                 break;
600
601         case DHCP6_STATE_STOPPED:
602         case DHCP6_STATE_RS:
603                 return 0;
604         }
605
606         if (r >= 0) {
607                 log_dhcp6_client(client, "Recv %s",
608                                  dhcp6_message_type_to_string(message->type));
609         }
610
611         return 0;
612 }
613
614 static int client_start(sd_dhcp6_client *client, enum DHCP6State state)
615 {
616         int r;
617
618         assert_return(client, -EINVAL);
619         assert_return(client->event, -EINVAL);
620         assert_return(client->index > 0, -EINVAL);
621         assert_return(client->state != state, -EINVAL);
622
623         client->timeout_resend_expire =
624                 sd_event_source_unref(client->timeout_resend_expire);
625         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
626         client->retransmit_time = 0;
627         client->retransmit_count = 0;
628
629         switch (state) {
630         case DHCP6_STATE_STOPPED:
631         case DHCP6_STATE_RS:
632         case DHCP6_STATE_SOLICITATION:
633
634                 r = client_ensure_iaid(client);
635                 if (r < 0)
636                         return r;
637
638                 r = dhcp6_network_bind_udp_socket(client->index, NULL);
639                 if (r < 0)
640                         return r;
641
642                 client->fd = r;
643
644                 r = sd_event_add_io(client->event, &client->receive_message,
645                                     client->fd, EPOLLIN, client_receive_message,
646                                     client);
647                 if (r < 0)
648                         return r;
649
650                 r = sd_event_source_set_priority(client->receive_message,
651                                                  client->event_priority);
652                 if (r < 0)
653                         return r;
654
655                 client->state = DHCP6_STATE_SOLICITATION;
656
657                 break;
658         }
659
660         client->transaction_id = random_u32() & htobe32(0x00ffffff);
661
662         r = sd_event_add_time(client->event, &client->timeout_resend,
663                               CLOCK_MONOTONIC, 0, 0, client_timeout_resend,
664                               client);
665         if (r < 0)
666                 return r;
667
668         r = sd_event_source_set_priority(client->timeout_resend,
669                                          client->event_priority);
670         if (r < 0)
671                 return r;
672
673         return 0;
674 }
675
676 int sd_dhcp6_client_stop(sd_dhcp6_client *client)
677 {
678         client_stop(client, DHCP6_EVENT_STOP);
679
680         return 0;
681 }
682
683 int sd_dhcp6_client_start(sd_dhcp6_client *client)
684 {
685         int r = 0;
686
687         assert_return(client, -EINVAL);
688         assert_return(client->event, -EINVAL);
689         assert_return(client->index > 0, -EINVAL);
690
691         r = client_initialize(client);
692         if (r < 0)
693                 return r;
694
695         return client_start(client, DHCP6_STATE_SOLICITATION);
696 }
697
698 int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event,
699                                  int priority)
700 {
701         int r;
702
703         assert_return(client, -EINVAL);
704         assert_return(!client->event, -EBUSY);
705
706         if (event)
707                 client->event = sd_event_ref(event);
708         else {
709                 r = sd_event_default(&client->event);
710                 if (r < 0)
711                         return 0;
712         }
713
714         client->event_priority = priority;
715
716         return 0;
717 }
718
719 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
720         assert_return(client, -EINVAL);
721
722         client->event = sd_event_unref(client->event);
723
724         return 0;
725 }
726
727 sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
728         if (!client)
729                 return NULL;
730
731         return client->event;
732 }
733
734 sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) {
735         if (client)
736                 assert_se(REFCNT_INC(client->n_ref) >= 2);
737
738         return client;
739 }
740
741 sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) {
742         if (client && REFCNT_DEC(client->n_ref) <= 0) {
743                 client_initialize(client);
744
745                 sd_dhcp6_client_detach_event(client);
746
747                 free(client);
748
749                 return NULL;
750         }
751
752         return client;
753 }
754
755 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref);
756 #define _cleanup_dhcp6_client_free_ _cleanup_(sd_dhcp6_client_unrefp)
757
758 int sd_dhcp6_client_new(sd_dhcp6_client **ret)
759 {
760         _cleanup_dhcp6_client_free_ sd_dhcp6_client *client = NULL;
761         sd_id128_t machine_id;
762         int r;
763
764         assert_return(ret, -EINVAL);
765
766         client = new0(sd_dhcp6_client, 1);
767         if (!client)
768                 return -ENOMEM;
769
770         client->n_ref = REFCNT_INIT;
771
772         client->ia_na.type = DHCP6_OPTION_IA_NA;
773
774         client->index = -1;
775
776         /* initialize DUID */
777         client->duid.type = htobe16(DHCP6_DUID_EN);
778         client->duid.pen = htobe32(SYSTEMD_PEN);
779
780         r = sd_id128_get_machine(&machine_id);
781         if (r < 0)
782                 return r;
783
784         /* a bit of snake-oil perhaps, but no need to expose the machine-id
785            directly */
786         siphash24(client->duid.id, &machine_id, sizeof(machine_id),
787                   HASH_KEY.bytes);
788
789         *ret = client;
790         client = NULL;
791
792         return 0;
793 }