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