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