Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 : : ***/
5 : :
6 : : #include <errno.h>
7 : : #include <string.h>
8 : : #include <sys/ioctl.h>
9 : : #include <linux/if_arp.h>
10 : : #include <linux/if_infiniband.h>
11 : :
12 : : #include "sd-dhcp6-client.h"
13 : :
14 : : #include "alloc-util.h"
15 : : #include "dhcp-identifier.h"
16 : : #include "dhcp6-internal.h"
17 : : #include "dhcp6-lease-internal.h"
18 : : #include "dhcp6-protocol.h"
19 : : #include "dns-domain.h"
20 : : #include "event-util.h"
21 : : #include "fd-util.h"
22 : : #include "hostname-util.h"
23 : : #include "in-addr-util.h"
24 : : #include "network-internal.h"
25 : : #include "random-util.h"
26 : : #include "socket-util.h"
27 : : #include "string-table.h"
28 : : #include "util.h"
29 : :
30 : : #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
31 : :
32 : : /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
33 : : enum {
34 : : DHCP6_REQUEST_IA_NA = 1,
35 : : DHCP6_REQUEST_IA_TA = 2, /* currently not used */
36 : : DHCP6_REQUEST_IA_PD = 4,
37 : : };
38 : :
39 : : struct sd_dhcp6_client {
40 : : unsigned n_ref;
41 : :
42 : : enum DHCP6State state;
43 : : sd_event *event;
44 : : int event_priority;
45 : : int ifindex;
46 : : struct in6_addr local_address;
47 : : uint8_t mac_addr[MAX_MAC_ADDR_LEN];
48 : : size_t mac_addr_len;
49 : : uint16_t arp_type;
50 : : DHCP6IA ia_na;
51 : : DHCP6IA ia_pd;
52 : : sd_event_source *timeout_t1;
53 : : sd_event_source *timeout_t2;
54 : : unsigned request;
55 : : be32_t transaction_id;
56 : : usec_t transaction_start;
57 : : struct sd_dhcp6_lease *lease;
58 : : int fd;
59 : : bool information_request;
60 : : bool iaid_set;
61 : : be16_t *req_opts;
62 : : size_t req_opts_allocated;
63 : : size_t req_opts_len;
64 : : char *fqdn;
65 : : sd_event_source *receive_message;
66 : : usec_t retransmit_time;
67 : : uint8_t retransmit_count;
68 : : sd_event_source *timeout_resend;
69 : : sd_event_source *timeout_resend_expire;
70 : : sd_dhcp6_client_callback_t callback;
71 : : void *userdata;
72 : : struct duid duid;
73 : : size_t duid_len;
74 : : };
75 : :
76 : : static const uint16_t default_req_opts[] = {
77 : : SD_DHCP6_OPTION_DNS_SERVERS,
78 : : SD_DHCP6_OPTION_DOMAIN_LIST,
79 : : SD_DHCP6_OPTION_NTP_SERVER,
80 : : SD_DHCP6_OPTION_SNTP_SERVERS,
81 : : };
82 : :
83 : : const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
84 : : [DHCP6_SOLICIT] = "SOLICIT",
85 : : [DHCP6_ADVERTISE] = "ADVERTISE",
86 : : [DHCP6_REQUEST] = "REQUEST",
87 : : [DHCP6_CONFIRM] = "CONFIRM",
88 : : [DHCP6_RENEW] = "RENEW",
89 : : [DHCP6_REBIND] = "REBIND",
90 : : [DHCP6_REPLY] = "REPLY",
91 : : [DHCP6_RELEASE] = "RELEASE",
92 : : [DHCP6_DECLINE] = "DECLINE",
93 : : [DHCP6_RECONFIGURE] = "RECONFIGURE",
94 : : [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
95 : : [DHCP6_RELAY_FORW] = "RELAY-FORW",
96 : : [DHCP6_RELAY_REPL] = "RELAY-REPL",
97 : : };
98 : :
99 [ + + + + ]: 152 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
100 : :
101 : : const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
102 : : [DHCP6_STATUS_SUCCESS] = "Success",
103 : : [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
104 : : [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
105 : : [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
106 : : [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
107 : : [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
108 : : };
109 : :
110 [ + + + + ]: 64 : DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
111 : :
112 : : #define DHCP6_CLIENT_DONT_DESTROY(client) \
113 : : _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
114 : :
115 : : static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
116 : :
117 : 16 : int sd_dhcp6_client_set_callback(
118 : : sd_dhcp6_client *client,
119 : : sd_dhcp6_client_callback_t cb,
120 : : void *userdata) {
121 : :
122 [ - + - + ]: 16 : assert_return(client, -EINVAL);
123 : :
124 : 16 : client->callback = cb;
125 : 16 : client->userdata = userdata;
126 : :
127 : 16 : return 0;
128 : : }
129 : :
130 : 20 : int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
131 : :
132 [ - + - + ]: 20 : assert_return(client, -EINVAL);
133 [ + + + + ]: 20 : assert_return(ifindex >= -1, -EINVAL);
134 [ + - - + : 16 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- + ]
135 : :
136 : 16 : client->ifindex = ifindex;
137 : 16 : return 0;
138 : : }
139 : :
140 : 8 : int sd_dhcp6_client_set_local_address(
141 : : sd_dhcp6_client *client,
142 : : const struct in6_addr *local_address) {
143 : :
144 [ - + - + ]: 8 : assert_return(client, -EINVAL);
145 [ - + - + ]: 8 : assert_return(local_address, -EINVAL);
146 [ - + - + ]: 8 : assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL);
147 : :
148 [ + - - + : 8 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- + ]
149 : :
150 : 8 : client->local_address = *local_address;
151 : :
152 : 8 : return 0;
153 : : }
154 : :
155 : 8 : int sd_dhcp6_client_set_mac(
156 : : sd_dhcp6_client *client,
157 : : const uint8_t *addr, size_t addr_len,
158 : : uint16_t arp_type) {
159 : :
160 [ - + - + ]: 8 : assert_return(client, -EINVAL);
161 [ - + - + ]: 8 : assert_return(addr, -EINVAL);
162 [ + - - + : 8 : assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
- + ]
163 [ - + - + ]: 8 : assert_return(arp_type > 0, -EINVAL);
164 : :
165 [ + - - + : 8 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
- + ]
166 : :
167 [ + - ]: 8 : if (arp_type == ARPHRD_ETHER)
168 [ - + - + ]: 8 : assert_return(addr_len == ETH_ALEN, -EINVAL);
169 [ # # ]: 0 : else if (arp_type == ARPHRD_INFINIBAND)
170 [ # # # # ]: 0 : assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
171 : : else
172 : 0 : return -EINVAL;
173 : :
174 [ - + ]: 8 : if (client->mac_addr_len == addr_len &&
175 [ # # ]: 0 : memcmp(&client->mac_addr, addr, addr_len) == 0)
176 : 0 : return 0;
177 : :
178 : 8 : memcpy(&client->mac_addr, addr, addr_len);
179 : 8 : client->mac_addr_len = addr_len;
180 : 8 : client->arp_type = arp_type;
181 : :
182 : 8 : return 0;
183 : : }
184 : :
185 : 8 : static int client_ensure_duid(sd_dhcp6_client *client) {
186 [ + + ]: 8 : if (client->duid_len != 0)
187 : 4 : return 0;
188 : :
189 : 4 : return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
190 : : }
191 : :
192 : : /**
193 : : * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid
194 : : * without further modification. Otherwise, if duid_type is supported, DUID
195 : : * is set based on that type. Otherwise, an error is returned.
196 : : */
197 : 0 : static int dhcp6_client_set_duid_internal(
198 : : sd_dhcp6_client *client,
199 : : uint16_t duid_type,
200 : : const void *duid,
201 : : size_t duid_len,
202 : : usec_t llt_time) {
203 : : int r;
204 : :
205 [ # # # # ]: 0 : assert_return(client, -EINVAL);
206 [ # # # # : 0 : assert_return(duid_len == 0 || duid != NULL, -EINVAL);
# # ]
207 [ # # # # : 0 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
# # ]
208 : :
209 [ # # ]: 0 : if (duid != NULL) {
210 : 0 : r = dhcp_validate_duid_len(duid_type, duid_len, true);
211 [ # # ]: 0 : if (r < 0) {
212 : 0 : r = dhcp_validate_duid_len(duid_type, duid_len, false);
213 [ # # ]: 0 : if (r < 0)
214 : 0 : return r;
215 : 0 : log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
216 : : }
217 : :
218 : 0 : client->duid.type = htobe16(duid_type);
219 : 0 : memcpy(&client->duid.raw.data, duid, duid_len);
220 : 0 : client->duid_len = sizeof(client->duid.type) + duid_len;
221 : : } else
222 [ # # # # : 0 : switch (duid_type) {
# ]
223 : 0 : case DUID_TYPE_LLT:
224 [ # # ]: 0 : if (client->mac_addr_len == 0)
225 : 0 : return -EOPNOTSUPP;
226 : :
227 : 0 : r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
228 [ # # ]: 0 : if (r < 0)
229 : 0 : return r;
230 : 0 : break;
231 : 0 : case DUID_TYPE_EN:
232 : 0 : r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
233 [ # # ]: 0 : if (r < 0)
234 : 0 : return r;
235 : 0 : break;
236 : 0 : case DUID_TYPE_LL:
237 [ # # ]: 0 : if (client->mac_addr_len == 0)
238 : 0 : return -EOPNOTSUPP;
239 : :
240 : 0 : r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len);
241 [ # # ]: 0 : if (r < 0)
242 : 0 : return r;
243 : 0 : break;
244 : 0 : case DUID_TYPE_UUID:
245 : 0 : r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
246 [ # # ]: 0 : if (r < 0)
247 : 0 : return r;
248 : 0 : break;
249 : 0 : default:
250 : 0 : return -EINVAL;
251 : : }
252 : :
253 : 0 : return 0;
254 : : }
255 : :
256 : 0 : int sd_dhcp6_client_set_duid(
257 : : sd_dhcp6_client *client,
258 : : uint16_t duid_type,
259 : : const void *duid,
260 : : size_t duid_len) {
261 : 0 : return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0);
262 : : }
263 : :
264 : 0 : int sd_dhcp6_client_set_duid_llt(
265 : : sd_dhcp6_client *client,
266 : : usec_t llt_time) {
267 : 0 : return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
268 : : }
269 : :
270 : 0 : int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
271 [ # # # # ]: 0 : assert_return(client, -EINVAL);
272 [ # # # # : 0 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
# # ]
273 : :
274 : 0 : client->ia_na.ia_na.id = htobe32(iaid);
275 : 0 : client->ia_pd.ia_pd.id = htobe32(iaid);
276 : 0 : client->iaid_set = true;
277 : :
278 : 0 : return 0;
279 : : }
280 : :
281 : 24 : int sd_dhcp6_client_set_fqdn(
282 : : sd_dhcp6_client *client,
283 : : const char *fqdn) {
284 : :
285 [ - + - + ]: 24 : assert_return(client, -EINVAL);
286 : :
287 : : /* Make sure FQDN qualifies as DNS and as Linux hostname */
288 [ + + ]: 24 : if (fqdn &&
289 [ + + - + ]: 20 : !(hostname_is_valid(fqdn, false) && dns_name_is_valid(fqdn) > 0))
290 : 8 : return -EINVAL;
291 : :
292 : 16 : return free_and_strdup(&client->fqdn, fqdn);
293 : : }
294 : :
295 : 20 : int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) {
296 [ - + - + ]: 20 : assert_return(client, -EINVAL);
297 [ + + + + : 20 : assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
+ + ]
298 : :
299 : 16 : client->information_request = enabled;
300 : :
301 : 16 : return 0;
302 : : }
303 : :
304 : 16 : int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled) {
305 [ - + - + ]: 16 : assert_return(client, -EINVAL);
306 [ - + - + ]: 16 : assert_return(enabled, -EINVAL);
307 : :
308 : 16 : *enabled = client->information_request;
309 : :
310 : 16 : return 0;
311 : : }
312 : :
313 : 28 : int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) {
314 : : size_t t;
315 : :
316 [ - + - + ]: 28 : assert_return(client, -EINVAL);
317 [ + + + + ]: 28 : assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
318 : :
319 [ + + ]: 24 : switch(option) {
320 : :
321 : 16 : case SD_DHCP6_OPTION_DNS_SERVERS:
322 : : case SD_DHCP6_OPTION_DOMAIN_LIST:
323 : : case SD_DHCP6_OPTION_SNTP_SERVERS:
324 : : case SD_DHCP6_OPTION_NTP_SERVER:
325 : : case SD_DHCP6_OPTION_RAPID_COMMIT:
326 : 16 : break;
327 : :
328 : 8 : default:
329 : 8 : return -EINVAL;
330 : : }
331 : :
332 [ + - ]: 40 : for (t = 0; t < client->req_opts_len; t++)
333 [ + + ]: 40 : if (client->req_opts[t] == htobe16(option))
334 : 16 : return -EEXIST;
335 : :
336 [ # # ]: 0 : if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
337 : : client->req_opts_len + 1))
338 : 0 : return -ENOMEM;
339 : :
340 : 0 : client->req_opts[client->req_opts_len++] = htobe16(option);
341 : :
342 : 0 : return 0;
343 : : }
344 : :
345 : 4 : int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
346 [ - + - + ]: 4 : assert_return(client, -EINVAL);
347 [ - + - + ]: 4 : assert_return(delegation, -EINVAL);
348 : :
349 : 4 : *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
350 : :
351 : 4 : return 0;
352 : : }
353 : :
354 : 4 : int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
355 [ - + - + ]: 4 : assert_return(client, -EINVAL);
356 : :
357 [ + - ]: 4 : SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
358 : :
359 : 4 : return 0;
360 : : }
361 : :
362 : 16 : int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
363 [ - + - + ]: 16 : assert_return(client, -EINVAL);
364 [ - + - + ]: 16 : assert_return(request, -EINVAL);
365 : :
366 : 16 : *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
367 : :
368 : 16 : return 0;
369 : : }
370 : :
371 : 12 : int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
372 [ - + - + ]: 12 : assert_return(client, -EINVAL);
373 : :
374 [ + - ]: 12 : SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
375 : :
376 : 12 : return 0;
377 : : }
378 : :
379 : 0 : int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) {
380 [ # # # # ]: 0 : assert_return(client, -EINVAL);
381 : :
382 : 0 : client->transaction_id = transaction_id;
383 : :
384 : 0 : return 0;
385 : : }
386 : :
387 : 8 : int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
388 [ - + - + ]: 8 : assert_return(client, -EINVAL);
389 : :
390 [ - + ]: 8 : if (!client->lease)
391 : 0 : return -ENOMSG;
392 : :
393 [ + - ]: 8 : if (ret)
394 : 8 : *ret = client->lease;
395 : :
396 : 8 : return 0;
397 : : }
398 : :
399 : 12 : static void client_notify(sd_dhcp6_client *client, int event) {
400 [ - + ]: 12 : assert(client);
401 : :
402 [ + + ]: 12 : if (client->callback)
403 : 8 : client->callback(client, event, client->userdata);
404 : 12 : }
405 : :
406 : 20 : static int client_reset(sd_dhcp6_client *client) {
407 [ - + ]: 20 : assert(client);
408 : :
409 : 20 : client->lease = sd_dhcp6_lease_unref(client->lease);
410 : :
411 : 20 : client->receive_message =
412 : 20 : sd_event_source_unref(client->receive_message);
413 : :
414 : 20 : client->transaction_id = 0;
415 : 20 : client->transaction_start = 0;
416 : :
417 : 20 : client->retransmit_time = 0;
418 : 20 : client->retransmit_count = 0;
419 : :
420 : 20 : (void) event_source_disable(client->timeout_resend);
421 : 20 : (void) event_source_disable(client->timeout_resend_expire);
422 : 20 : (void) event_source_disable(client->timeout_t1);
423 : 20 : (void) event_source_disable(client->timeout_t2);
424 : :
425 : 20 : client->state = DHCP6_STATE_STOPPED;
426 : :
427 : 20 : return 0;
428 : : }
429 : :
430 : 4 : static void client_stop(sd_dhcp6_client *client, int error) {
431 : 8 : DHCP6_CLIENT_DONT_DESTROY(client);
432 : :
433 [ - + ]: 4 : assert(client);
434 : :
435 : 4 : client_notify(client, error);
436 : :
437 : 4 : client_reset(client);
438 : 4 : }
439 : :
440 : 12 : static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
441 : 12 : _cleanup_free_ DHCP6Message *message = NULL;
442 : 12 : struct in6_addr all_servers =
443 : : IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
444 : 12 : size_t len, optlen = 512;
445 : : uint8_t *opt;
446 : : int r;
447 : : usec_t elapsed_usec;
448 : : be16_t elapsed_time;
449 : :
450 [ - + ]: 12 : assert(client);
451 : :
452 : 12 : len = sizeof(DHCP6Message) + optlen;
453 : :
454 : 12 : message = malloc0(len);
455 [ - + ]: 12 : if (!message)
456 : 0 : return -ENOMEM;
457 : :
458 : 12 : opt = (uint8_t *)(message + 1);
459 : :
460 : 12 : message->transaction_id = client->transaction_id;
461 : :
462 [ + + + - : 12 : switch(client->state) {
- - ]
463 : 4 : case DHCP6_STATE_INFORMATION_REQUEST:
464 : 4 : message->type = DHCP6_INFORMATION_REQUEST;
465 : :
466 : 4 : break;
467 : :
468 : 4 : case DHCP6_STATE_SOLICITATION:
469 : 4 : message->type = DHCP6_SOLICIT;
470 : :
471 : 4 : r = dhcp6_option_append(&opt, &optlen,
472 : : SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
473 [ - + ]: 4 : if (r < 0)
474 : 0 : return r;
475 : :
476 [ + - ]: 4 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
477 : 4 : r = dhcp6_option_append_ia(&opt, &optlen,
478 : 4 : &client->ia_na);
479 [ - + ]: 4 : if (r < 0)
480 : 0 : return r;
481 : : }
482 : :
483 [ + - ]: 4 : if (client->fqdn) {
484 : 4 : r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
485 [ - + ]: 4 : if (r < 0)
486 : 0 : return r;
487 : : }
488 : :
489 [ - + ]: 4 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
490 : 0 : r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd);
491 [ # # ]: 0 : if (r < 0)
492 : 0 : return r;
493 : :
494 : 0 : opt += r;
495 : 0 : optlen -= r;
496 : : }
497 : :
498 : 4 : break;
499 : :
500 : 4 : case DHCP6_STATE_REQUEST:
501 : : case DHCP6_STATE_RENEW:
502 : :
503 [ + - ]: 4 : if (client->state == DHCP6_STATE_REQUEST)
504 : 4 : message->type = DHCP6_REQUEST;
505 : : else
506 : 0 : message->type = DHCP6_RENEW;
507 : :
508 : 4 : r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
509 : 4 : client->lease->serverid_len,
510 : 4 : client->lease->serverid);
511 [ - + ]: 4 : if (r < 0)
512 : 0 : return r;
513 : :
514 [ + - ]: 4 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
515 : 4 : r = dhcp6_option_append_ia(&opt, &optlen,
516 : 4 : &client->lease->ia);
517 [ - + ]: 4 : if (r < 0)
518 : 0 : return r;
519 : : }
520 : :
521 [ + - ]: 4 : if (client->fqdn) {
522 : 4 : r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
523 [ - + ]: 4 : if (r < 0)
524 : 0 : return r;
525 : : }
526 : :
527 [ - + ]: 4 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
528 : 0 : r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
529 [ # # ]: 0 : if (r < 0)
530 : 0 : return r;
531 : :
532 : 0 : opt += r;
533 : 0 : optlen -= r;
534 : : }
535 : :
536 : 4 : break;
537 : :
538 : 0 : case DHCP6_STATE_REBIND:
539 : 0 : message->type = DHCP6_REBIND;
540 : :
541 [ # # ]: 0 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
542 : 0 : r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
543 [ # # ]: 0 : if (r < 0)
544 : 0 : return r;
545 : : }
546 : :
547 [ # # ]: 0 : if (client->fqdn) {
548 : 0 : r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn);
549 [ # # ]: 0 : if (r < 0)
550 : 0 : return r;
551 : : }
552 : :
553 [ # # ]: 0 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
554 : 0 : r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd);
555 [ # # ]: 0 : if (r < 0)
556 : 0 : return r;
557 : :
558 : 0 : opt += r;
559 : 0 : optlen -= r;
560 : : }
561 : :
562 : 0 : break;
563 : :
564 : 0 : case DHCP6_STATE_STOPPED:
565 : : case DHCP6_STATE_BOUND:
566 : 0 : return -EINVAL;
567 : : }
568 : :
569 : 24 : r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
570 : 12 : client->req_opts_len * sizeof(be16_t),
571 : 12 : client->req_opts);
572 [ - + ]: 12 : if (r < 0)
573 : 0 : return r;
574 : :
575 [ - + ]: 12 : assert(client->duid_len);
576 : 12 : r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_CLIENTID,
577 : 12 : client->duid_len, &client->duid);
578 [ - + ]: 12 : if (r < 0)
579 : 0 : return r;
580 : :
581 : 12 : elapsed_usec = time_now - client->transaction_start;
582 [ + - ]: 12 : if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
583 : 12 : elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
584 : : else
585 : 0 : elapsed_time = 0xffff;
586 : :
587 : 12 : r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
588 : : sizeof(elapsed_time), &elapsed_time);
589 [ - + ]: 12 : if (r < 0)
590 : 0 : return r;
591 : :
592 : 12 : r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
593 : : len - optlen);
594 [ - + ]: 12 : if (r < 0)
595 : 0 : return r;
596 : :
597 : 12 : log_dhcp6_client(client, "Sent %s",
598 : : dhcp6_message_type_to_string(message->type));
599 : :
600 : 12 : return 0;
601 : : }
602 : :
603 : 0 : static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
604 : 0 : sd_dhcp6_client *client = userdata;
605 : :
606 [ # # ]: 0 : assert(s);
607 [ # # ]: 0 : assert(client);
608 [ # # ]: 0 : assert(client->lease);
609 : :
610 : 0 : (void) event_source_disable(client->timeout_t2);
611 : :
612 : 0 : log_dhcp6_client(client, "Timeout T2");
613 : :
614 : 0 : client_start(client, DHCP6_STATE_REBIND);
615 : :
616 : 0 : return 0;
617 : : }
618 : :
619 : 0 : static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
620 : 0 : sd_dhcp6_client *client = userdata;
621 : :
622 [ # # ]: 0 : assert(s);
623 [ # # ]: 0 : assert(client);
624 [ # # ]: 0 : assert(client->lease);
625 : :
626 : 0 : (void) event_source_disable(client->timeout_t1);
627 : :
628 : 0 : log_dhcp6_client(client, "Timeout T1");
629 : :
630 : 0 : client_start(client, DHCP6_STATE_RENEW);
631 : :
632 : 0 : return 0;
633 : : }
634 : :
635 : 0 : static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
636 : 0 : sd_dhcp6_client *client = userdata;
637 : 0 : DHCP6_CLIENT_DONT_DESTROY(client);
638 : : enum DHCP6State state;
639 : :
640 [ # # ]: 0 : assert(s);
641 [ # # ]: 0 : assert(client);
642 [ # # ]: 0 : assert(client->event);
643 : :
644 : 0 : state = client->state;
645 : :
646 : 0 : client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE);
647 : :
648 : : /* RFC 3315, section 18.1.4., says that "...the client may choose to
649 : : use a Solicit message to locate a new DHCP server..." */
650 [ # # ]: 0 : if (state == DHCP6_STATE_REBIND)
651 : 0 : client_start(client, DHCP6_STATE_SOLICITATION);
652 : :
653 : 0 : return 0;
654 : : }
655 : :
656 : 20 : static usec_t client_timeout_compute_random(usec_t val) {
657 : 60 : return val - val / 10 +
658 : 20 : (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
659 : : }
660 : :
661 : 16 : static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) {
662 : 16 : int r = 0;
663 : 16 : sd_dhcp6_client *client = userdata;
664 : 16 : usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0;
665 : 16 : usec_t max_retransmit_duration = 0;
666 : 16 : uint8_t max_retransmit_count = 0;
667 : : char time_string[FORMAT_TIMESPAN_MAX];
668 : 16 : uint32_t expire = 0;
669 : :
670 [ - + ]: 16 : assert(s);
671 [ - + ]: 16 : assert(client);
672 [ - + ]: 16 : assert(client->event);
673 : :
674 : 16 : (void) event_source_disable(client->timeout_resend);
675 : :
676 [ + + + - : 16 : switch (client->state) {
- - - ]
677 : 4 : case DHCP6_STATE_INFORMATION_REQUEST:
678 : 4 : init_retransmit_time = DHCP6_INF_TIMEOUT;
679 : 4 : max_retransmit_time = DHCP6_INF_MAX_RT;
680 : :
681 : 4 : break;
682 : :
683 : 8 : case DHCP6_STATE_SOLICITATION:
684 : :
685 [ + + + - ]: 8 : if (client->retransmit_count && client->lease) {
686 : 4 : client_start(client, DHCP6_STATE_REQUEST);
687 : 4 : return 0;
688 : : }
689 : :
690 : 4 : init_retransmit_time = DHCP6_SOL_TIMEOUT;
691 : 4 : max_retransmit_time = DHCP6_SOL_MAX_RT;
692 : :
693 : 4 : break;
694 : :
695 : 4 : case DHCP6_STATE_REQUEST:
696 : 4 : init_retransmit_time = DHCP6_REQ_TIMEOUT;
697 : 4 : max_retransmit_time = DHCP6_REQ_MAX_RT;
698 : 4 : max_retransmit_count = DHCP6_REQ_MAX_RC;
699 : :
700 : 4 : break;
701 : :
702 : 0 : case DHCP6_STATE_RENEW:
703 : 0 : init_retransmit_time = DHCP6_REN_TIMEOUT;
704 : 0 : max_retransmit_time = DHCP6_REN_MAX_RT;
705 : :
706 : : /* RFC 3315, section 18.1.3. says max retransmit duration will
707 : : be the remaining time until T2. Instead of setting MRD,
708 : : wait for T2 to trigger with the same end result */
709 : :
710 : 0 : break;
711 : :
712 : 0 : case DHCP6_STATE_REBIND:
713 : 0 : init_retransmit_time = DHCP6_REB_TIMEOUT;
714 : 0 : max_retransmit_time = DHCP6_REB_MAX_RT;
715 : :
716 [ # # ]: 0 : if (event_source_is_enabled(client->timeout_resend_expire) <= 0) {
717 : 0 : r = dhcp6_lease_ia_rebind_expire(&client->lease->ia,
718 : : &expire);
719 [ # # ]: 0 : if (r < 0) {
720 : 0 : client_stop(client, r);
721 : 0 : return 0;
722 : : }
723 : 0 : max_retransmit_duration = expire * USEC_PER_SEC;
724 : : }
725 : :
726 : 0 : break;
727 : :
728 : 0 : case DHCP6_STATE_STOPPED:
729 : : case DHCP6_STATE_BOUND:
730 : 0 : return 0;
731 : : }
732 : :
733 [ + + ]: 12 : if (max_retransmit_count &&
734 [ - + ]: 4 : client->retransmit_count >= max_retransmit_count) {
735 : 0 : client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX);
736 : 0 : return 0;
737 : : }
738 : :
739 : 12 : r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
740 [ - + ]: 12 : if (r < 0)
741 : 0 : goto error;
742 : :
743 : 12 : r = client_send_message(client, time_now);
744 [ + - ]: 12 : if (r >= 0)
745 : 12 : client->retransmit_count++;
746 : :
747 [ + - ]: 12 : if (!client->retransmit_time) {
748 : 12 : client->retransmit_time =
749 : 12 : client_timeout_compute_random(init_retransmit_time);
750 : :
751 [ + + ]: 12 : if (client->state == DHCP6_STATE_SOLICITATION)
752 : 4 : client->retransmit_time += init_retransmit_time / 10;
753 : :
754 : : } else {
755 [ # # ]: 0 : if (max_retransmit_time &&
756 [ # # ]: 0 : client->retransmit_time > max_retransmit_time / 2)
757 : 0 : client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
758 : : else
759 : 0 : client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
760 : : }
761 : :
762 : 12 : log_dhcp6_client(client, "Next retransmission in %s",
763 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, client->retransmit_time, USEC_PER_SEC));
764 : :
765 : 12 : r = event_reset_time(client->event, &client->timeout_resend,
766 : : clock_boottime_or_monotonic(),
767 : 12 : time_now + client->retransmit_time, 10 * USEC_PER_MSEC,
768 : : client_timeout_resend, client,
769 : 12 : client->event_priority, "dhcp6-resend-timer", true);
770 [ - + ]: 12 : if (r < 0)
771 : 0 : goto error;
772 : :
773 [ + - # # ]: 12 : if (max_retransmit_duration && event_source_is_enabled(client->timeout_resend_expire) <= 0) {
774 : :
775 : 0 : log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs",
776 : : max_retransmit_duration / USEC_PER_SEC);
777 : :
778 : 0 : r = event_reset_time(client->event, &client->timeout_resend_expire,
779 : : clock_boottime_or_monotonic(),
780 : : time_now + max_retransmit_duration, USEC_PER_SEC,
781 : : client_timeout_resend_expire, client,
782 : 0 : client->event_priority, "dhcp6-resend-expire-timer", true);
783 [ # # ]: 0 : if (r < 0)
784 : 0 : goto error;
785 : : }
786 : :
787 : 12 : error:
788 [ - + ]: 12 : if (r < 0)
789 : 0 : client_stop(client, r);
790 : :
791 : 12 : return 0;
792 : : }
793 : :
794 : 8 : static int client_ensure_iaid(sd_dhcp6_client *client) {
795 : : int r;
796 : : uint32_t iaid;
797 : :
798 [ - + ]: 8 : assert(client);
799 : :
800 [ + + ]: 8 : if (client->iaid_set)
801 : 4 : return 0;
802 : :
803 : 4 : r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, true, &iaid);
804 [ - + ]: 4 : if (r < 0)
805 : 0 : return r;
806 : :
807 : 4 : client->ia_na.ia_na.id = iaid;
808 : 4 : client->ia_pd.ia_pd.id = iaid;
809 : 4 : client->iaid_set = true;
810 : :
811 : 4 : return 0;
812 : : }
813 : :
814 : 12 : static int client_parse_message(
815 : : sd_dhcp6_client *client,
816 : : DHCP6Message *message,
817 : : size_t len,
818 : : sd_dhcp6_lease *lease) {
819 : :
820 : 12 : uint32_t lt_t1 = ~0, lt_t2 = ~0;
821 : 12 : bool clientid = false;
822 : 12 : size_t pos = 0;
823 : : int r;
824 : :
825 [ - + ]: 12 : assert(client);
826 [ - + ]: 12 : assert(message);
827 [ - + ]: 12 : assert(len >= sizeof(DHCP6Message));
828 [ - + ]: 12 : assert(lease);
829 : :
830 : 12 : len -= sizeof(DHCP6Message);
831 : :
832 [ + + ]: 88 : while (pos < len) {
833 : 76 : DHCP6Option *option = (DHCP6Option *) &message->options[pos];
834 : : uint16_t optcode, optlen;
835 : : be32_t iaid_lease;
836 : : uint8_t *optval;
837 : : int status;
838 : :
839 [ - + ]: 76 : if (len < pos + offsetof(DHCP6Option, data))
840 : 0 : return -ENOBUFS;
841 : :
842 : 76 : optcode = be16toh(option->code);
843 : 76 : optlen = be16toh(option->len);
844 : 76 : optval = option->data;
845 : :
846 [ - + ]: 76 : if (len < pos + offsetof(DHCP6Option, data) + optlen)
847 : 0 : return -ENOBUFS;
848 : :
849 [ + + + - : 76 : switch (optcode) {
+ - - + +
- + - ]
850 : 12 : case SD_DHCP6_OPTION_CLIENTID:
851 [ - + ]: 12 : if (clientid) {
852 : 0 : log_dhcp6_client(client, "%s contains multiple clientids",
853 : : dhcp6_message_type_to_string(message->type));
854 : 0 : return -EINVAL;
855 : : }
856 : :
857 [ + - ]: 12 : if (optlen != client->duid_len ||
858 [ - + ]: 12 : memcmp(&client->duid, optval, optlen) != 0) {
859 : 0 : log_dhcp6_client(client, "%s DUID does not match",
860 : : dhcp6_message_type_to_string(message->type));
861 : :
862 : 0 : return -EINVAL;
863 : : }
864 : 12 : clientid = true;
865 : :
866 : 12 : break;
867 : :
868 : 12 : case SD_DHCP6_OPTION_SERVERID:
869 : 12 : r = dhcp6_lease_get_serverid(lease, NULL, NULL);
870 [ - + ]: 12 : if (r >= 0) {
871 : 0 : log_dhcp6_client(client, "%s contains multiple serverids",
872 : : dhcp6_message_type_to_string(message->type));
873 : 0 : return -EINVAL;
874 : : }
875 : :
876 : 12 : r = dhcp6_lease_set_serverid(lease, optval, optlen);
877 [ - + ]: 12 : if (r < 0)
878 : 0 : return r;
879 : :
880 : 12 : break;
881 : :
882 : 4 : case SD_DHCP6_OPTION_PREFERENCE:
883 [ - + ]: 4 : if (optlen != 1)
884 : 0 : return -EINVAL;
885 : :
886 : 4 : r = dhcp6_lease_set_preference(lease, optval[0]);
887 [ - + ]: 4 : if (r < 0)
888 : 0 : return r;
889 : :
890 : 4 : break;
891 : :
892 : 0 : case SD_DHCP6_OPTION_STATUS_CODE:
893 : 0 : status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
894 [ # # ]: 0 : if (status < 0)
895 : 0 : return status;
896 : :
897 [ # # ]: 0 : if (status > 0) {
898 : 0 : log_dhcp6_client(client, "%s Status %s",
899 : : dhcp6_message_type_to_string(message->type),
900 : : dhcp6_message_status_to_string(status));
901 : :
902 : 0 : return -EINVAL;
903 : : }
904 : :
905 : 0 : break;
906 : :
907 : 12 : case SD_DHCP6_OPTION_IA_NA:
908 [ + + ]: 12 : if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
909 : 4 : log_dhcp6_client(client, "Information request ignoring IA NA option");
910 : :
911 : 4 : break;
912 : : }
913 : :
914 : 8 : r = dhcp6_option_parse_ia(option, &lease->ia);
915 [ - + # # ]: 8 : if (r < 0 && r != -ENOMSG)
916 : 0 : return r;
917 : :
918 : 8 : r = dhcp6_lease_get_iaid(lease, &iaid_lease);
919 [ - + ]: 8 : if (r < 0)
920 : 0 : return r;
921 : :
922 [ - + ]: 8 : if (client->ia_na.ia_na.id != iaid_lease) {
923 : 0 : log_dhcp6_client(client, "%s has wrong IAID for IA NA",
924 : : dhcp6_message_type_to_string(message->type));
925 : 0 : return -EINVAL;
926 : : }
927 : :
928 [ + - ]: 8 : if (lease->ia.addresses) {
929 : 8 : lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
930 : 8 : lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
931 : : }
932 : :
933 : 8 : break;
934 : :
935 : 0 : case SD_DHCP6_OPTION_IA_PD:
936 [ # # ]: 0 : if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
937 : 0 : log_dhcp6_client(client, "Information request ignoring IA PD option");
938 : :
939 : 0 : break;
940 : : }
941 : :
942 : 0 : r = dhcp6_option_parse_ia(option, &lease->pd);
943 [ # # # # ]: 0 : if (r < 0 && r != -ENOMSG)
944 : 0 : return r;
945 : :
946 : 0 : r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
947 [ # # ]: 0 : if (r < 0)
948 : 0 : return r;
949 : :
950 [ # # ]: 0 : if (client->ia_pd.ia_pd.id != iaid_lease) {
951 : 0 : log_dhcp6_client(client, "%s has wrong IAID for IA PD",
952 : : dhcp6_message_type_to_string(message->type));
953 : 0 : return -EINVAL;
954 : : }
955 : :
956 [ # # ]: 0 : if (lease->pd.addresses) {
957 : 0 : lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
958 : 0 : lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
959 : : }
960 : :
961 : 0 : break;
962 : :
963 : 0 : case SD_DHCP6_OPTION_RAPID_COMMIT:
964 : 0 : r = dhcp6_lease_set_rapid_commit(lease);
965 [ # # ]: 0 : if (r < 0)
966 : 0 : return r;
967 : :
968 : 0 : break;
969 : :
970 : 12 : case SD_DHCP6_OPTION_DNS_SERVERS:
971 : 12 : r = dhcp6_lease_set_dns(lease, optval, optlen);
972 [ - + ]: 12 : if (r < 0)
973 : 0 : return r;
974 : :
975 : 12 : break;
976 : :
977 : 12 : case SD_DHCP6_OPTION_DOMAIN_LIST:
978 : 12 : r = dhcp6_lease_set_domains(lease, optval, optlen);
979 [ - + ]: 12 : if (r < 0)
980 : 0 : return r;
981 : :
982 : 12 : break;
983 : :
984 : 0 : case SD_DHCP6_OPTION_NTP_SERVER:
985 : 0 : r = dhcp6_lease_set_ntp(lease, optval, optlen);
986 [ # # ]: 0 : if (r < 0)
987 : 0 : return r;
988 : :
989 : 0 : break;
990 : :
991 : 12 : case SD_DHCP6_OPTION_SNTP_SERVERS:
992 : 12 : r = dhcp6_lease_set_sntp(lease, optval, optlen);
993 [ - + ]: 12 : if (r < 0)
994 : 0 : return r;
995 : :
996 : 12 : break;
997 : : }
998 : :
999 : 76 : pos += offsetof(DHCP6Option, data) + optlen;
1000 : : }
1001 : :
1002 [ - + ]: 12 : if (!clientid) {
1003 : 0 : log_dhcp6_client(client, "%s has incomplete options",
1004 : : dhcp6_message_type_to_string(message->type));
1005 : 0 : return -EINVAL;
1006 : : }
1007 : :
1008 [ + + ]: 12 : if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
1009 : 8 : r = dhcp6_lease_get_serverid(lease, NULL, NULL);
1010 [ - + ]: 8 : if (r < 0) {
1011 : 0 : log_dhcp6_client(client, "%s has no server id",
1012 : : dhcp6_message_type_to_string(message->type));
1013 : 0 : return -EINVAL;
1014 : : }
1015 : :
1016 : : } else {
1017 [ - + ]: 4 : if (lease->ia.addresses) {
1018 : 0 : lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
1019 : 0 : lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
1020 : : }
1021 : :
1022 [ - + ]: 4 : if (lease->pd.addresses) {
1023 : 0 : lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
1024 : 0 : lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
1025 : : }
1026 : : }
1027 : :
1028 : 12 : return 0;
1029 : : }
1030 : :
1031 : 12 : static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
1032 : 12 : _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1033 : : bool rapid_commit;
1034 : : int r;
1035 : :
1036 [ - + ]: 12 : assert(client);
1037 [ - + ]: 12 : assert(reply);
1038 : :
1039 [ + + ]: 12 : if (reply->type != DHCP6_REPLY)
1040 : 4 : return 0;
1041 : :
1042 : 8 : r = dhcp6_lease_new(&lease);
1043 [ - + ]: 8 : if (r < 0)
1044 : 0 : return -ENOMEM;
1045 : :
1046 : 8 : r = client_parse_message(client, reply, len, lease);
1047 [ - + ]: 8 : if (r < 0)
1048 : 0 : return r;
1049 : :
1050 [ - + ]: 8 : if (client->state == DHCP6_STATE_SOLICITATION) {
1051 : 0 : r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
1052 [ # # ]: 0 : if (r < 0)
1053 : 0 : return r;
1054 : :
1055 [ # # ]: 0 : if (!rapid_commit)
1056 : 0 : return 0;
1057 : : }
1058 : :
1059 : 8 : sd_dhcp6_lease_unref(client->lease);
1060 : 8 : client->lease = TAKE_PTR(lease);
1061 : :
1062 : 8 : return DHCP6_STATE_BOUND;
1063 : : }
1064 : :
1065 : 4 : static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
1066 : 4 : _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
1067 : 4 : uint8_t pref_advertise = 0, pref_lease = 0;
1068 : : int r;
1069 : :
1070 [ - + ]: 4 : if (advertise->type != DHCP6_ADVERTISE)
1071 : 0 : return 0;
1072 : :
1073 : 4 : r = dhcp6_lease_new(&lease);
1074 [ - + ]: 4 : if (r < 0)
1075 : 0 : return r;
1076 : :
1077 : 4 : r = client_parse_message(client, advertise, len, lease);
1078 [ - + ]: 4 : if (r < 0)
1079 : 0 : return r;
1080 : :
1081 : 4 : r = dhcp6_lease_get_preference(lease, &pref_advertise);
1082 [ - + ]: 4 : if (r < 0)
1083 : 0 : return r;
1084 : :
1085 : 4 : r = dhcp6_lease_get_preference(client->lease, &pref_lease);
1086 : :
1087 [ - + # # ]: 4 : if (r < 0 || pref_advertise > pref_lease) {
1088 : 4 : sd_dhcp6_lease_unref(client->lease);
1089 : 4 : client->lease = TAKE_PTR(lease);
1090 : 4 : r = 0;
1091 : : }
1092 : :
1093 [ + - - + ]: 4 : if (pref_advertise == 255 || client->retransmit_count > 1)
1094 : 0 : r = DHCP6_STATE_REQUEST;
1095 : :
1096 : 4 : return r;
1097 : : }
1098 : :
1099 : 12 : static int client_receive_message(
1100 : : sd_event_source *s,
1101 : : int fd, uint32_t
1102 : : revents,
1103 : : void *userdata) {
1104 : :
1105 : 12 : sd_dhcp6_client *client = userdata;
1106 : 24 : DHCP6_CLIENT_DONT_DESTROY(client);
1107 : 12 : _cleanup_free_ DHCP6Message *message = NULL;
1108 : : ssize_t buflen, len;
1109 : 12 : int r = 0;
1110 : :
1111 [ - + ]: 12 : assert(s);
1112 [ - + ]: 12 : assert(client);
1113 [ - + ]: 12 : assert(client->event);
1114 : :
1115 : 12 : buflen = next_datagram_size_fd(fd);
1116 [ - + ]: 12 : if (buflen == -ENETDOWN) {
1117 : : /* the link is down. Don't return an error or the I/O event
1118 : : source will be disconnected and we won't be able to receive
1119 : : packets again when the link comes back. */
1120 : 0 : return 0;
1121 : : }
1122 [ - + ]: 12 : if (buflen < 0)
1123 : 0 : return buflen;
1124 : :
1125 : 12 : message = malloc(buflen);
1126 [ - + ]: 12 : if (!message)
1127 : 0 : return -ENOMEM;
1128 : :
1129 : 12 : len = recv(fd, message, buflen, 0);
1130 [ - + ]: 12 : if (len < 0) {
1131 : : /* see comment above for why we shouldn't error out on ENETDOWN. */
1132 [ # # # # ]: 0 : if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
1133 : 0 : return 0;
1134 : :
1135 : 0 : return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
1136 : :
1137 : : }
1138 [ - + ]: 12 : if ((size_t) len < sizeof(DHCP6Message)) {
1139 : 0 : log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
1140 : 0 : return 0;
1141 : : }
1142 : :
1143 [ - + - ]: 12 : switch(message->type) {
1144 : 0 : case DHCP6_SOLICIT:
1145 : : case DHCP6_REQUEST:
1146 : : case DHCP6_CONFIRM:
1147 : : case DHCP6_RENEW:
1148 : : case DHCP6_REBIND:
1149 : : case DHCP6_RELEASE:
1150 : : case DHCP6_DECLINE:
1151 : : case DHCP6_INFORMATION_REQUEST:
1152 : : case DHCP6_RELAY_FORW:
1153 : : case DHCP6_RELAY_REPL:
1154 : 0 : return 0;
1155 : :
1156 : 12 : case DHCP6_ADVERTISE:
1157 : : case DHCP6_REPLY:
1158 : : case DHCP6_RECONFIGURE:
1159 : 12 : break;
1160 : :
1161 : 0 : default:
1162 : 0 : log_dhcp6_client(client, "Unknown message type %d", message->type);
1163 : 0 : return 0;
1164 : : }
1165 : :
1166 [ - + ]: 12 : if (client->transaction_id != (message->transaction_id &
1167 : 12 : htobe32(0x00ffffff)))
1168 : 0 : return 0;
1169 : :
1170 [ + + + - : 12 : switch (client->state) {
- - ]
1171 : 4 : case DHCP6_STATE_INFORMATION_REQUEST:
1172 : 4 : r = client_receive_reply(client, message, len);
1173 [ - + ]: 4 : if (r < 0)
1174 : 0 : return 0;
1175 : :
1176 : 4 : client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
1177 : :
1178 : 4 : client_start(client, DHCP6_STATE_STOPPED);
1179 : :
1180 : 4 : break;
1181 : :
1182 : 4 : case DHCP6_STATE_SOLICITATION:
1183 : 4 : r = client_receive_advertise(client, message, len);
1184 : :
1185 [ - + ]: 4 : if (r == DHCP6_STATE_REQUEST) {
1186 : 0 : client_start(client, r);
1187 : :
1188 : 0 : break;
1189 : : }
1190 : :
1191 : : _fallthrough_; /* for Soliciation Rapid Commit option check */
1192 : : case DHCP6_STATE_REQUEST:
1193 : : case DHCP6_STATE_RENEW:
1194 : : case DHCP6_STATE_REBIND:
1195 : :
1196 : 8 : r = client_receive_reply(client, message, len);
1197 [ - + ]: 8 : if (r < 0)
1198 : 0 : return 0;
1199 : :
1200 [ + + ]: 8 : if (r == DHCP6_STATE_BOUND) {
1201 : :
1202 : 4 : r = client_start(client, DHCP6_STATE_BOUND);
1203 [ - + ]: 4 : if (r < 0) {
1204 : 0 : client_stop(client, r);
1205 : 0 : return 0;
1206 : : }
1207 : :
1208 : 4 : client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
1209 : : }
1210 : :
1211 : 8 : break;
1212 : :
1213 : 0 : case DHCP6_STATE_BOUND:
1214 : :
1215 : 0 : break;
1216 : :
1217 : 0 : case DHCP6_STATE_STOPPED:
1218 : 0 : return 0;
1219 : : }
1220 : :
1221 : 12 : log_dhcp6_client(client, "Recv %s",
1222 : : dhcp6_message_type_to_string(message->type));
1223 : :
1224 : 12 : return 0;
1225 : : }
1226 : :
1227 : 4 : static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
1228 : : uint32_t *lifetime_t2) {
1229 [ - + - + ]: 4 : assert_return(client, -EINVAL);
1230 [ - + - + ]: 4 : assert_return(client->lease, -EINVAL);
1231 : :
1232 [ + - + - ]: 4 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
1233 : 4 : *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
1234 : 4 : *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
1235 : :
1236 : 4 : return 0;
1237 : : }
1238 : :
1239 [ # # # # ]: 0 : if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
1240 : 0 : *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
1241 : 0 : *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
1242 : :
1243 : 0 : return 0;
1244 : : }
1245 : :
1246 : 0 : return -ENOMSG;
1247 : : }
1248 : :
1249 : 20 : static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
1250 : : int r;
1251 : : usec_t timeout, time_now;
1252 : : char time_string[FORMAT_TIMESPAN_MAX];
1253 : : uint32_t lifetime_t1, lifetime_t2;
1254 : :
1255 [ - + - + ]: 20 : assert_return(client, -EINVAL);
1256 [ - + - + ]: 20 : assert_return(client->event, -EINVAL);
1257 [ - + - + ]: 20 : assert_return(client->ifindex > 0, -EINVAL);
1258 [ - + - + ]: 20 : assert_return(client->state != state, -EINVAL);
1259 : :
1260 : 20 : (void) event_source_disable(client->timeout_resend_expire);
1261 : 20 : (void) event_source_disable(client->timeout_resend);
1262 : 20 : client->retransmit_time = 0;
1263 : 20 : client->retransmit_count = 0;
1264 : :
1265 : 20 : r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1266 [ - + ]: 20 : if (r < 0)
1267 : 0 : return r;
1268 : :
1269 [ + + ]: 20 : if (!client->receive_message) {
1270 : 8 : r = sd_event_add_io(client->event, &client->receive_message,
1271 : : client->fd, EPOLLIN, client_receive_message,
1272 : : client);
1273 [ - + ]: 8 : if (r < 0)
1274 : 0 : goto error;
1275 : :
1276 : 8 : r = sd_event_source_set_priority(client->receive_message,
1277 : 8 : client->event_priority);
1278 [ - + ]: 8 : if (r < 0)
1279 : 0 : goto error;
1280 : :
1281 : 8 : r = sd_event_source_set_description(client->receive_message,
1282 : : "dhcp6-receive-message");
1283 [ - + ]: 8 : if (r < 0)
1284 : 0 : goto error;
1285 : : }
1286 : :
1287 [ + + + + : 20 : switch (state) {
- ]
1288 : 4 : case DHCP6_STATE_STOPPED:
1289 [ - + ]: 4 : if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
1290 : 0 : client->state = DHCP6_STATE_STOPPED;
1291 : :
1292 : 0 : return 0;
1293 : : }
1294 : :
1295 : : _fallthrough_;
1296 : : case DHCP6_STATE_SOLICITATION:
1297 : 8 : client->state = DHCP6_STATE_SOLICITATION;
1298 : :
1299 : 8 : break;
1300 : :
1301 : 8 : case DHCP6_STATE_INFORMATION_REQUEST:
1302 : : case DHCP6_STATE_REQUEST:
1303 : : case DHCP6_STATE_RENEW:
1304 : : case DHCP6_STATE_REBIND:
1305 : :
1306 : 8 : client->state = state;
1307 : :
1308 : 8 : break;
1309 : :
1310 : 4 : case DHCP6_STATE_BOUND:
1311 : :
1312 : 4 : r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2);
1313 [ - + ]: 4 : if (r < 0)
1314 : 0 : goto error;
1315 : :
1316 [ + - - + ]: 4 : if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) {
1317 : 0 : log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x",
1318 : : lifetime_t1, lifetime_t2);
1319 : :
1320 : 0 : return 0;
1321 : : }
1322 : :
1323 : 4 : timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC);
1324 : :
1325 : 4 : log_dhcp6_client(client, "T1 expires in %s",
1326 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1327 : :
1328 : 4 : r = event_reset_time(client->event, &client->timeout_t1,
1329 : : clock_boottime_or_monotonic(),
1330 : : time_now + timeout, 10 * USEC_PER_SEC,
1331 : : client_timeout_t1, client,
1332 : 4 : client->event_priority, "dhcp6-t1-timeout", true);
1333 [ - + ]: 4 : if (r < 0)
1334 : 0 : goto error;
1335 : :
1336 : 4 : timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC);
1337 : :
1338 : 4 : log_dhcp6_client(client, "T2 expires in %s",
1339 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC));
1340 : :
1341 : 4 : r = event_reset_time(client->event, &client->timeout_t2,
1342 : : clock_boottime_or_monotonic(),
1343 : : time_now + timeout, 10 * USEC_PER_SEC,
1344 : : client_timeout_t2, client,
1345 : 4 : client->event_priority, "dhcp6-t2-timeout", true);
1346 [ - + ]: 4 : if (r < 0)
1347 : 0 : goto error;
1348 : :
1349 : 4 : client->state = state;
1350 : :
1351 : 4 : return 0;
1352 : : }
1353 : :
1354 : 16 : client->transaction_id = random_u32() & htobe32(0x00ffffff);
1355 : 16 : client->transaction_start = time_now;
1356 : :
1357 : 16 : r = event_reset_time(client->event, &client->timeout_resend,
1358 : : clock_boottime_or_monotonic(),
1359 : : 0, 0,
1360 : : client_timeout_resend, client,
1361 : 16 : client->event_priority, "dhcp6-resend-timeout", true);
1362 [ - + ]: 16 : if (r < 0)
1363 : 0 : goto error;
1364 : :
1365 : 16 : return 0;
1366 : :
1367 : 0 : error:
1368 : 0 : client_reset(client);
1369 : 0 : return r;
1370 : : }
1371 : :
1372 : 4 : int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
1373 [ - + - + ]: 4 : assert_return(client, -EINVAL);
1374 : :
1375 : 4 : client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
1376 : :
1377 : 4 : client->fd = safe_close(client->fd);
1378 : :
1379 : 4 : return 0;
1380 : : }
1381 : :
1382 : 0 : int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
1383 [ # # # # ]: 0 : assert_return(client, -EINVAL);
1384 : :
1385 : 0 : return client->state != DHCP6_STATE_STOPPED;
1386 : : }
1387 : :
1388 : 8 : int sd_dhcp6_client_start(sd_dhcp6_client *client) {
1389 : 8 : enum DHCP6State state = DHCP6_STATE_SOLICITATION;
1390 : 8 : int r = 0;
1391 : :
1392 [ - + - + ]: 8 : assert_return(client, -EINVAL);
1393 [ - + - + ]: 8 : assert_return(client->event, -EINVAL);
1394 [ - + - + ]: 8 : assert_return(client->ifindex > 0, -EINVAL);
1395 [ - + - + ]: 8 : assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &client->local_address) > 0, -EINVAL);
1396 : :
1397 [ + - - + ]: 8 : if (!IN_SET(client->state, DHCP6_STATE_STOPPED))
1398 : 0 : return -EBUSY;
1399 : :
1400 [ + + - + ]: 8 : if (!client->information_request && !client->request)
1401 : 0 : return -EINVAL;
1402 : :
1403 : 8 : r = client_reset(client);
1404 [ - + ]: 8 : if (r < 0)
1405 : 0 : return r;
1406 : :
1407 : 8 : r = client_ensure_iaid(client);
1408 [ - + ]: 8 : if (r < 0)
1409 : 0 : return r;
1410 : :
1411 : 8 : r = client_ensure_duid(client);
1412 [ - + ]: 8 : if (r < 0)
1413 : 0 : return r;
1414 : :
1415 [ + - ]: 8 : if (client->fd < 0) {
1416 : 8 : r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address);
1417 [ - + ]: 8 : if (r < 0) {
1418 : 0 : _cleanup_free_ char *p = NULL;
1419 : :
1420 : 0 : (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p);
1421 : 0 : return log_dhcp6_client_errno(client, r,
1422 : : "Failed to bind to UDP socket at address %s: %m", strna(p));
1423 : : }
1424 : :
1425 : 8 : client->fd = r;
1426 : : }
1427 : :
1428 [ + + ]: 8 : if (client->information_request)
1429 : 4 : state = DHCP6_STATE_INFORMATION_REQUEST;
1430 : :
1431 [ + + ]: 8 : log_dhcp6_client(client, "Started in %s mode",
1432 : : client->information_request? "Information request":
1433 : : "Managed");
1434 : :
1435 : 8 : return client_start(client, state);
1436 : : }
1437 : :
1438 : 8 : int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) {
1439 : : int r;
1440 : :
1441 [ - + - + ]: 8 : assert_return(client, -EINVAL);
1442 [ - + - + ]: 8 : assert_return(!client->event, -EBUSY);
1443 : :
1444 [ + - ]: 8 : if (event)
1445 : 8 : client->event = sd_event_ref(event);
1446 : : else {
1447 : 0 : r = sd_event_default(&client->event);
1448 [ # # ]: 0 : if (r < 0)
1449 : 0 : return 0;
1450 : : }
1451 : :
1452 : 8 : client->event_priority = priority;
1453 : :
1454 : 8 : return 0;
1455 : : }
1456 : :
1457 : 12 : int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) {
1458 [ - + - + ]: 12 : assert_return(client, -EINVAL);
1459 : :
1460 : 12 : client->event = sd_event_unref(client->event);
1461 : :
1462 : 12 : return 0;
1463 : : }
1464 : :
1465 : 0 : sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) {
1466 [ # # # # ]: 0 : assert_return(client, NULL);
1467 : :
1468 : 0 : return client->event;
1469 : : }
1470 : :
1471 : 8 : static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
1472 [ - + ]: 8 : assert(client);
1473 : :
1474 : 8 : client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1475 : 8 : client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire);
1476 : 8 : client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1477 : 8 : client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1478 : :
1479 : 8 : client_reset(client);
1480 : :
1481 : 8 : client->fd = safe_close(client->fd);
1482 : :
1483 : 8 : sd_dhcp6_client_detach_event(client);
1484 : :
1485 : 8 : free(client->req_opts);
1486 : 8 : free(client->fqdn);
1487 : 8 : return mfree(client);
1488 : : }
1489 : :
1490 [ + + - + : 68 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_client, sd_dhcp6_client, dhcp6_client_free);
+ + ]
1491 : :
1492 : 8 : int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
1493 : 8 : _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
1494 : 8 : _cleanup_free_ be16_t *req_opts = NULL;
1495 : : size_t t;
1496 : :
1497 [ - + - + ]: 8 : assert_return(ret, -EINVAL);
1498 : :
1499 : 8 : req_opts = new(be16_t, ELEMENTSOF(default_req_opts));
1500 [ - + ]: 8 : if (!req_opts)
1501 : 0 : return -ENOMEM;
1502 : :
1503 [ + + ]: 40 : for (t = 0; t < ELEMENTSOF(default_req_opts); t++)
1504 : 32 : req_opts[t] = htobe16(default_req_opts[t]);
1505 : :
1506 : 8 : client = new(sd_dhcp6_client, 1);
1507 [ - + ]: 8 : if (!client)
1508 : 0 : return -ENOMEM;
1509 : :
1510 : 16 : *client = (sd_dhcp6_client) {
1511 : : .n_ref = 1,
1512 : : .ia_na.type = SD_DHCP6_OPTION_IA_NA,
1513 : : .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
1514 : : .ifindex = -1,
1515 : : .request = DHCP6_REQUEST_IA_NA,
1516 : : .fd = -1,
1517 : : .req_opts_len = ELEMENTSOF(default_req_opts),
1518 : 8 : .req_opts = TAKE_PTR(req_opts),
1519 : : };
1520 : :
1521 : 8 : *ret = TAKE_PTR(client);
1522 : :
1523 : 8 : return 0;
1524 : : }
|