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