Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2013 Intel Corporation. All rights reserved.
4 : : ***/
5 : :
6 : : #include <errno.h>
7 : : #include <net/ethernet.h>
8 : : #include <net/if_arp.h>
9 : : #include <stdio.h>
10 : : #include <stdlib.h>
11 : : #include <string.h>
12 : : #include <sys/ioctl.h>
13 : : #include <linux/if_infiniband.h>
14 : :
15 : : #include "sd-dhcp-client.h"
16 : :
17 : : #include "alloc-util.h"
18 : : #include "async.h"
19 : : #include "dhcp-identifier.h"
20 : : #include "dhcp-internal.h"
21 : : #include "dhcp-lease-internal.h"
22 : : #include "dhcp-protocol.h"
23 : : #include "dns-domain.h"
24 : : #include "event-util.h"
25 : : #include "hostname-util.h"
26 : : #include "io-util.h"
27 : : #include "memory-util.h"
28 : : #include "random-util.h"
29 : : #include "string-util.h"
30 : : #include "strv.h"
31 : :
32 : : #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
33 : : #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
34 : :
35 : : #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
36 : : #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
37 : :
38 : : struct sd_dhcp_client {
39 : : unsigned n_ref;
40 : :
41 : : DHCPState state;
42 : : sd_event *event;
43 : : int event_priority;
44 : : sd_event_source *timeout_resend;
45 : : int ifindex;
46 : : int fd;
47 : : uint16_t port;
48 : : union sockaddr_union link;
49 : : sd_event_source *receive_message;
50 : : bool request_broadcast;
51 : : uint8_t *req_opts;
52 : : size_t req_opts_allocated;
53 : : size_t req_opts_size;
54 : : bool anonymize;
55 : : be32_t last_addr;
56 : : uint8_t mac_addr[MAX_MAC_ADDR_LEN];
57 : : size_t mac_addr_len;
58 : : uint16_t arp_type;
59 : : struct {
60 : : uint8_t type;
61 : : union {
62 : : struct {
63 : : /* 0: Generic (non-LL) (RFC 2132) */
64 : : uint8_t data[MAX_CLIENT_ID_LEN];
65 : : } _packed_ gen;
66 : : struct {
67 : : /* 1: Ethernet Link-Layer (RFC 2132) */
68 : : uint8_t haddr[ETH_ALEN];
69 : : } _packed_ eth;
70 : : struct {
71 : : /* 2 - 254: ARP/Link-Layer (RFC 2132) */
72 : : uint8_t haddr[0];
73 : : } _packed_ ll;
74 : : struct {
75 : : /* 255: Node-specific (RFC 4361) */
76 : : be32_t iaid;
77 : : struct duid duid;
78 : : } _packed_ ns;
79 : : struct {
80 : : uint8_t data[MAX_CLIENT_ID_LEN];
81 : : } _packed_ raw;
82 : : };
83 : : } _packed_ client_id;
84 : : size_t client_id_len;
85 : : char *hostname;
86 : : char *vendor_class_identifier;
87 : : char **user_class;
88 : : uint32_t mtu;
89 : : uint32_t xid;
90 : : usec_t start_time;
91 : : uint64_t attempt;
92 : : uint64_t max_attempts;
93 : : usec_t request_sent;
94 : : sd_event_source *timeout_t1;
95 : : sd_event_source *timeout_t2;
96 : : sd_event_source *timeout_expire;
97 : : sd_dhcp_client_callback_t callback;
98 : : void *userdata;
99 : : sd_dhcp_lease *lease;
100 : : usec_t start_delay;
101 : : };
102 : :
103 : : static const uint8_t default_req_opts[] = {
104 : : SD_DHCP_OPTION_SUBNET_MASK,
105 : : SD_DHCP_OPTION_ROUTER,
106 : : SD_DHCP_OPTION_HOST_NAME,
107 : : SD_DHCP_OPTION_DOMAIN_NAME,
108 : : SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
109 : : };
110 : :
111 : : /* RFC7844 section 3:
112 : : MAY contain the Parameter Request List option.
113 : : RFC7844 section 3.6:
114 : : The client intending to protect its privacy SHOULD only request a
115 : : minimal number of options in the PRL and SHOULD also randomly shuffle
116 : : the ordering of option codes in the PRL. If this random ordering
117 : : cannot be implemented, the client MAY order the option codes in the
118 : : PRL by option code number (lowest to highest).
119 : : */
120 : : /* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
121 : : static const uint8_t default_req_opts_anonymize[] = {
122 : : SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
123 : : SD_DHCP_OPTION_ROUTER, /* 3 */
124 : : SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
125 : : SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
126 : : SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
127 : : SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
128 : : SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
129 : : SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
130 : : SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
131 : : SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
132 : : SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
133 : : SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
134 : : SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
135 : : };
136 : :
137 : : static int client_receive_message_raw(
138 : : sd_event_source *s,
139 : : int fd,
140 : : uint32_t revents,
141 : : void *userdata);
142 : : static int client_receive_message_udp(
143 : : sd_event_source *s,
144 : : int fd,
145 : : uint32_t revents,
146 : : void *userdata);
147 : : static void client_stop(sd_dhcp_client *client, int error);
148 : :
149 : 8 : int sd_dhcp_client_set_callback(
150 : : sd_dhcp_client *client,
151 : : sd_dhcp_client_callback_t cb,
152 : : void *userdata) {
153 : :
154 [ - + - + ]: 8 : assert_return(client, -EINVAL);
155 : :
156 : 8 : client->callback = cb;
157 : 8 : client->userdata = userdata;
158 : :
159 : 8 : return 0;
160 : : }
161 : :
162 : 0 : int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
163 [ # # # # ]: 0 : assert_return(client, -EINVAL);
164 : :
165 : 0 : client->request_broadcast = !!broadcast;
166 : :
167 : 0 : return 0;
168 : : }
169 : :
170 : 84 : int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
171 : : size_t i;
172 : :
173 [ + + + + ]: 84 : assert_return(client, -EINVAL);
174 [ + - - + : 80 : assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
- + ]
175 : :
176 [ + + ]: 80 : switch(option) {
177 : :
178 : 24 : case SD_DHCP_OPTION_PAD:
179 : : case SD_DHCP_OPTION_OVERLOAD:
180 : : case SD_DHCP_OPTION_MESSAGE_TYPE:
181 : : case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
182 : : case SD_DHCP_OPTION_END:
183 : 24 : return -EINVAL;
184 : :
185 : 56 : default:
186 : 56 : break;
187 : : }
188 : :
189 [ + + ]: 392 : for (i = 0; i < client->req_opts_size; i++)
190 [ + + ]: 372 : if (client->req_opts[i] == option)
191 : 36 : return -EEXIST;
192 : :
193 [ - + ]: 20 : if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
194 : : client->req_opts_size + 1))
195 : 0 : return -ENOMEM;
196 : :
197 : 20 : client->req_opts[client->req_opts_size++] = option;
198 : :
199 : 20 : return 0;
200 : : }
201 : :
202 : 4 : int sd_dhcp_client_set_request_address(
203 : : sd_dhcp_client *client,
204 : : const struct in_addr *last_addr) {
205 : :
206 [ + - + - ]: 4 : assert_return(client, -EINVAL);
207 [ # # # # : 0 : assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
# # ]
208 : :
209 [ # # ]: 0 : if (last_addr)
210 : 0 : client->last_addr = last_addr->s_addr;
211 : : else
212 : 0 : client->last_addr = INADDR_ANY;
213 : :
214 : 0 : return 0;
215 : : }
216 : :
217 : 32 : int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
218 : :
219 [ + + + + ]: 32 : assert_return(client, -EINVAL);
220 [ + - - + : 28 : assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
- + ]
221 [ + + + + ]: 28 : assert_return(ifindex > 0, -EINVAL);
222 : :
223 : 16 : client->ifindex = ifindex;
224 : 16 : return 0;
225 : : }
226 : :
227 : 8 : int sd_dhcp_client_set_mac(
228 : : sd_dhcp_client *client,
229 : : const uint8_t *addr,
230 : : size_t addr_len,
231 : : uint16_t arp_type) {
232 : :
233 : 16 : DHCP_CLIENT_DONT_DESTROY(client);
234 : 8 : bool need_restart = false;
235 : :
236 [ - + - + ]: 8 : assert_return(client, -EINVAL);
237 [ - + - + ]: 8 : assert_return(addr, -EINVAL);
238 [ + - - + : 8 : assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
- + ]
239 [ - + - + ]: 8 : assert_return(arp_type > 0, -EINVAL);
240 : :
241 [ + - ]: 8 : if (arp_type == ARPHRD_ETHER)
242 [ - + - + ]: 8 : assert_return(addr_len == ETH_ALEN, -EINVAL);
243 [ # # ]: 0 : else if (arp_type == ARPHRD_INFINIBAND)
244 [ # # # # ]: 0 : assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
245 : : else
246 : 0 : return -EINVAL;
247 : :
248 [ - + ]: 8 : if (client->mac_addr_len == addr_len &&
249 [ # # ]: 0 : memcmp(&client->mac_addr, addr, addr_len) == 0)
250 : 0 : return 0;
251 : :
252 [ + - - + ]: 8 : if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
253 : 0 : log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
254 : 0 : need_restart = true;
255 : 0 : client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
256 : : }
257 : :
258 : 8 : memcpy(&client->mac_addr, addr, addr_len);
259 : 8 : client->mac_addr_len = addr_len;
260 : 8 : client->arp_type = arp_type;
261 : :
262 [ - + # # ]: 8 : if (need_restart && client->state != DHCP_STATE_STOPPED)
263 : 0 : sd_dhcp_client_start(client);
264 : :
265 : 8 : return 0;
266 : : }
267 : :
268 : 0 : int sd_dhcp_client_get_client_id(
269 : : sd_dhcp_client *client,
270 : : uint8_t *type,
271 : : const uint8_t **data,
272 : : size_t *data_len) {
273 : :
274 [ # # # # ]: 0 : assert_return(client, -EINVAL);
275 [ # # # # ]: 0 : assert_return(type, -EINVAL);
276 [ # # # # ]: 0 : assert_return(data, -EINVAL);
277 [ # # # # ]: 0 : assert_return(data_len, -EINVAL);
278 : :
279 : 0 : *type = 0;
280 : 0 : *data = NULL;
281 : 0 : *data_len = 0;
282 [ # # ]: 0 : if (client->client_id_len) {
283 : 0 : *type = client->client_id.type;
284 : 0 : *data = client->client_id.raw.data;
285 : 0 : *data_len = client->client_id_len - sizeof(client->client_id.type);
286 : : }
287 : :
288 : 0 : return 0;
289 : : }
290 : :
291 : 0 : int sd_dhcp_client_set_client_id(
292 : : sd_dhcp_client *client,
293 : : uint8_t type,
294 : : const uint8_t *data,
295 : : size_t data_len) {
296 : :
297 : 0 : DHCP_CLIENT_DONT_DESTROY(client);
298 : 0 : bool need_restart = false;
299 : :
300 [ # # # # ]: 0 : assert_return(client, -EINVAL);
301 [ # # # # ]: 0 : assert_return(data, -EINVAL);
302 [ # # # # : 0 : assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
# # ]
303 : :
304 [ # # ]: 0 : if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
305 [ # # ]: 0 : client->client_id.type == type &&
306 [ # # ]: 0 : memcmp(&client->client_id.raw.data, data, data_len) == 0)
307 : 0 : return 0;
308 : :
309 : : /* For hardware types, log debug message about unexpected data length.
310 : : *
311 : : * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
312 : : * last last 8 bytes of the address are stable and suitable to put into
313 : : * the client-id. The caller is advised to account for that. */
314 [ # # # # : 0 : if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
# # ]
315 [ # # ]: 0 : (type == ARPHRD_INFINIBAND && data_len != 8))
316 : 0 : log_dhcp_client(client, "Changing client ID to hardware type %u with "
317 : : "unexpected address length %zu",
318 : : type, data_len);
319 : :
320 [ # # # # ]: 0 : if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
321 : 0 : log_dhcp_client(client, "Changing client ID on running DHCP "
322 : : "client, restarting");
323 : 0 : need_restart = true;
324 : 0 : client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
325 : : }
326 : :
327 : 0 : client->client_id.type = type;
328 : 0 : memcpy(&client->client_id.raw.data, data, data_len);
329 : 0 : client->client_id_len = data_len + sizeof (client->client_id.type);
330 : :
331 [ # # # # ]: 0 : if (need_restart && client->state != DHCP_STATE_STOPPED)
332 : 0 : sd_dhcp_client_start(client);
333 : :
334 : 0 : return 0;
335 : : }
336 : :
337 : : /**
338 : : * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
339 : : * without further modification. Otherwise, if duid_type is supported, DUID
340 : : * is set based on that type. Otherwise, an error is returned.
341 : : */
342 : 0 : static int dhcp_client_set_iaid_duid_internal(
343 : : sd_dhcp_client *client,
344 : : bool iaid_append,
345 : : bool iaid_set,
346 : : uint32_t iaid,
347 : : uint16_t duid_type,
348 : : const void *duid,
349 : : size_t duid_len,
350 : : usec_t llt_time) {
351 : :
352 : 0 : DHCP_CLIENT_DONT_DESTROY(client);
353 : : int r;
354 : : size_t len;
355 : :
356 [ # # # # ]: 0 : assert_return(client, -EINVAL);
357 [ # # # # : 0 : assert_return(duid_len == 0 || duid, -EINVAL);
# # ]
358 : :
359 [ # # ]: 0 : if (duid) {
360 : 0 : r = dhcp_validate_duid_len(duid_type, duid_len, true);
361 [ # # ]: 0 : if (r < 0)
362 : 0 : return r;
363 : : }
364 : :
365 [ # # ]: 0 : zero(client->client_id);
366 : 0 : client->client_id.type = 255;
367 : :
368 [ # # ]: 0 : if (iaid_append) {
369 [ # # ]: 0 : if (iaid_set)
370 : 0 : client->client_id.ns.iaid = htobe32(iaid);
371 : : else {
372 : 0 : r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
373 : : client->mac_addr_len,
374 : : true,
375 : 0 : &client->client_id.ns.iaid);
376 [ # # ]: 0 : if (r < 0)
377 : 0 : return r;
378 : : }
379 : : }
380 : :
381 [ # # ]: 0 : if (duid) {
382 : 0 : client->client_id.ns.duid.type = htobe16(duid_type);
383 : 0 : memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
384 : 0 : len = sizeof(client->client_id.ns.duid.type) + duid_len;
385 : : } else
386 [ # # # # : 0 : switch (duid_type) {
# ]
387 : 0 : case DUID_TYPE_LLT:
388 [ # # ]: 0 : if (client->mac_addr_len == 0)
389 : 0 : return -EOPNOTSUPP;
390 : :
391 : 0 : r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
392 [ # # ]: 0 : if (r < 0)
393 : 0 : return r;
394 : 0 : break;
395 : 0 : case DUID_TYPE_EN:
396 : 0 : r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
397 [ # # ]: 0 : if (r < 0)
398 : 0 : return r;
399 : 0 : break;
400 : 0 : case DUID_TYPE_LL:
401 [ # # ]: 0 : if (client->mac_addr_len == 0)
402 : 0 : return -EOPNOTSUPP;
403 : :
404 : 0 : r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
405 [ # # ]: 0 : if (r < 0)
406 : 0 : return r;
407 : 0 : break;
408 : 0 : case DUID_TYPE_UUID:
409 : 0 : r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
410 [ # # ]: 0 : if (r < 0)
411 : 0 : return r;
412 : 0 : break;
413 : 0 : default:
414 : 0 : return -EINVAL;
415 : : }
416 : :
417 : 0 : client->client_id_len = sizeof(client->client_id.type) + len +
418 [ # # ]: 0 : (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
419 : :
420 [ # # # # ]: 0 : if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
421 [ # # ]: 0 : log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
422 : 0 : client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
423 : 0 : sd_dhcp_client_start(client);
424 : : }
425 : :
426 : 0 : return 0;
427 : : }
428 : :
429 : 0 : int sd_dhcp_client_set_iaid_duid(
430 : : sd_dhcp_client *client,
431 : : bool iaid_set,
432 : : uint32_t iaid,
433 : : uint16_t duid_type,
434 : : const void *duid,
435 : : size_t duid_len) {
436 : 0 : return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
437 : : }
438 : :
439 : 0 : int sd_dhcp_client_set_iaid_duid_llt(
440 : : sd_dhcp_client *client,
441 : : bool iaid_set,
442 : : uint32_t iaid,
443 : : usec_t llt_time) {
444 : 0 : return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
445 : : }
446 : :
447 : 0 : int sd_dhcp_client_set_duid(
448 : : sd_dhcp_client *client,
449 : : uint16_t duid_type,
450 : : const void *duid,
451 : : size_t duid_len) {
452 : 0 : return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
453 : : }
454 : :
455 : 0 : int sd_dhcp_client_set_duid_llt(
456 : : sd_dhcp_client *client,
457 : : usec_t llt_time) {
458 : 0 : return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
459 : : }
460 : :
461 : 20 : int sd_dhcp_client_set_hostname(
462 : : sd_dhcp_client *client,
463 : : const char *hostname) {
464 : :
465 [ - + - + ]: 20 : assert_return(client, -EINVAL);
466 : :
467 : : /* Make sure hostnames qualify as DNS and as Linux hostnames */
468 [ + + ]: 20 : if (hostname &&
469 [ + + - + ]: 16 : !(hostname_is_valid(hostname, false) && dns_name_is_valid(hostname) > 0))
470 : 8 : return -EINVAL;
471 : :
472 : 12 : return free_and_strdup(&client->hostname, hostname);
473 : : }
474 : :
475 : 0 : int sd_dhcp_client_set_vendor_class_identifier(
476 : : sd_dhcp_client *client,
477 : : const char *vci) {
478 : :
479 [ # # # # ]: 0 : assert_return(client, -EINVAL);
480 : :
481 : 0 : return free_and_strdup(&client->vendor_class_identifier, vci);
482 : : }
483 : :
484 : 0 : int sd_dhcp_client_set_user_class(
485 : : sd_dhcp_client *client,
486 : : const char* const* user_class) {
487 : :
488 : 0 : _cleanup_strv_free_ char **s = NULL;
489 : : char **p;
490 : :
491 [ # # # # ]: 0 : STRV_FOREACH(p, (char **) user_class)
492 [ # # ]: 0 : if (strlen(*p) > 255)
493 : 0 : return -ENAMETOOLONG;
494 : :
495 : 0 : s = strv_copy((char **) user_class);
496 [ # # ]: 0 : if (!s)
497 : 0 : return -ENOMEM;
498 : :
499 : 0 : client->user_class = TAKE_PTR(s);
500 : :
501 : 0 : return 0;
502 : : }
503 : :
504 : 0 : int sd_dhcp_client_set_client_port(
505 : : sd_dhcp_client *client,
506 : : uint16_t port) {
507 : :
508 [ # # # # ]: 0 : assert_return(client, -EINVAL);
509 : :
510 : 0 : client->port = port;
511 : :
512 : 0 : return 0;
513 : : }
514 : :
515 : 0 : int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
516 [ # # # # ]: 0 : assert_return(client, -EINVAL);
517 [ # # # # ]: 0 : assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
518 : :
519 : 0 : client->mtu = mtu;
520 : :
521 : 0 : return 0;
522 : : }
523 : :
524 : 0 : int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
525 [ # # # # ]: 0 : assert_return(client, -EINVAL);
526 : :
527 : 0 : client->max_attempts = max_attempts;
528 : :
529 : 0 : return 0;
530 : : }
531 : :
532 : 4 : int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
533 [ - + - + ]: 4 : assert_return(client, -EINVAL);
534 : :
535 [ + - - + ]: 4 : if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
536 : 0 : return -EADDRNOTAVAIL;
537 : :
538 [ + - ]: 4 : if (ret)
539 : 4 : *ret = client->lease;
540 : :
541 : 4 : return 0;
542 : : }
543 : :
544 : 12 : static int client_notify(sd_dhcp_client *client, int event) {
545 [ - + ]: 12 : assert(client);
546 : :
547 [ + + ]: 12 : if (client->callback)
548 : 4 : return client->callback(client, event, client->userdata);
549 : :
550 : 8 : return 0;
551 : : }
552 : :
553 : 32 : static int client_initialize(sd_dhcp_client *client) {
554 [ - + - + ]: 32 : assert_return(client, -EINVAL);
555 : :
556 : 32 : client->receive_message = sd_event_source_unref(client->receive_message);
557 : :
558 : 32 : client->fd = asynchronous_close(client->fd);
559 : :
560 : 32 : (void) event_source_disable(client->timeout_resend);
561 : 32 : (void) event_source_disable(client->timeout_t1);
562 : 32 : (void) event_source_disable(client->timeout_t2);
563 : 32 : (void) event_source_disable(client->timeout_expire);
564 : :
565 : 32 : client->attempt = 0;
566 : :
567 : 32 : client->state = DHCP_STATE_INIT;
568 : 32 : client->xid = 0;
569 : :
570 : 32 : client->lease = sd_dhcp_lease_unref(client->lease);
571 : :
572 : 32 : return 0;
573 : : }
574 : :
575 : 8 : static void client_stop(sd_dhcp_client *client, int error) {
576 [ - + ]: 8 : assert(client);
577 : :
578 [ - + ]: 8 : if (error < 0)
579 : 0 : log_dhcp_client_errno(client, error, "STOPPED: %m");
580 [ + - ]: 8 : else if (error == SD_DHCP_CLIENT_EVENT_STOP)
581 : 8 : log_dhcp_client(client, "STOPPED");
582 : : else
583 : 0 : log_dhcp_client(client, "STOPPED: Unknown event");
584 : :
585 : 8 : client_notify(client, error);
586 : :
587 : 8 : client_initialize(client);
588 : 8 : }
589 : :
590 : 8 : static int client_message_init(
591 : : sd_dhcp_client *client,
592 : : DHCPPacket **ret,
593 : : uint8_t type,
594 : : size_t *_optlen,
595 : : size_t *_optoffset) {
596 : :
597 : 8 : _cleanup_free_ DHCPPacket *packet = NULL;
598 : : size_t optlen, optoffset, size;
599 : : be16_t max_size;
600 : : usec_t time_now;
601 : : uint16_t secs;
602 : : int r;
603 : :
604 [ - + ]: 8 : assert(client);
605 [ - + ]: 8 : assert(client->start_time);
606 [ - + ]: 8 : assert(ret);
607 [ - + ]: 8 : assert(_optlen);
608 [ - + ]: 8 : assert(_optoffset);
609 [ + - - + ]: 8 : assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE));
610 : :
611 : 8 : optlen = DHCP_MIN_OPTIONS_SIZE;
612 : 8 : size = sizeof(DHCPPacket) + optlen;
613 : :
614 : 8 : packet = malloc0(size);
615 [ - + ]: 8 : if (!packet)
616 : 0 : return -ENOMEM;
617 : :
618 : 8 : r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
619 : 8 : client->arp_type, optlen, &optoffset);
620 [ - + ]: 8 : if (r < 0)
621 : 0 : return r;
622 : :
623 : : /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
624 : : refuse to issue an DHCP lease if 'secs' is set to zero */
625 : 8 : r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
626 [ - + ]: 8 : if (r < 0)
627 : 0 : return r;
628 [ - + ]: 8 : assert(time_now >= client->start_time);
629 : :
630 : : /* seconds between sending first and last DISCOVER
631 : : * must always be strictly positive to deal with broken servers */
632 [ - + ]: 8 : secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
633 : 8 : packet->dhcp.secs = htobe16(secs);
634 : :
635 : : /* RFC2132 section 4.1
636 : : A client that cannot receive unicast IP datagrams until its protocol
637 : : software has been configured with an IP address SHOULD set the
638 : : BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
639 : : DHCPREQUEST messages that client sends. The BROADCAST bit will
640 : : provide a hint to the DHCP server and BOOTP relay agent to broadcast
641 : : any messages to the client on the client's subnet.
642 : :
643 : : Note: some interfaces needs this to be enabled, but some networks
644 : : needs this to be disabled as broadcasts are filteretd, so this
645 : : needs to be configurable */
646 [ + - - + ]: 8 : if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
647 : 0 : packet->dhcp.flags = htobe16(0x8000);
648 : :
649 : : /* RFC2132 section 4.1.1:
650 : : The client MUST include its hardware address in the ’chaddr’ field, if
651 : : necessary for delivery of DHCP reply messages. Non-Ethernet
652 : : interfaces will leave 'chaddr' empty and use the client identifier
653 : : instead (eg, RFC 4390 section 2.1).
654 : : */
655 [ + - ]: 8 : if (client->arp_type == ARPHRD_ETHER)
656 : 8 : memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
657 : :
658 : : /* If no client identifier exists, construct an RFC 4361-compliant one */
659 [ + - ]: 8 : if (client->client_id_len == 0) {
660 : : size_t duid_len;
661 : :
662 : 8 : client->client_id.type = 255;
663 : :
664 : 8 : r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
665 : 8 : true, &client->client_id.ns.iaid);
666 [ - + ]: 8 : if (r < 0)
667 : 0 : return r;
668 : :
669 : 8 : r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
670 [ - + ]: 8 : if (r < 0)
671 : 0 : return r;
672 : :
673 : 8 : client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
674 : : }
675 : :
676 : : /* Some DHCP servers will refuse to issue an DHCP lease if the Client
677 : : Identifier option is not set */
678 [ + - ]: 8 : if (client->client_id_len) {
679 : 8 : r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
680 : : SD_DHCP_OPTION_CLIENT_IDENTIFIER,
681 : : client->client_id_len,
682 : 8 : &client->client_id);
683 [ - + ]: 8 : if (r < 0)
684 : 0 : return r;
685 : : }
686 : :
687 : : /* RFC2131 section 3.5:
688 : : in its initial DHCPDISCOVER or DHCPREQUEST message, a
689 : : client may provide the server with a list of specific
690 : : parameters the client is interested in. If the client
691 : : includes a list of parameters in a DHCPDISCOVER message,
692 : : it MUST include that list in any subsequent DHCPREQUEST
693 : : messages.
694 : : */
695 : :
696 : : /* RFC7844 section 3:
697 : : MAY contain the Parameter Request List option. */
698 : : /* NOTE: in case that there would be an option to do not send
699 : : * any PRL at all, the size should be checked before sending */
700 [ + - + - ]: 8 : if (client->req_opts_size > 0 && type != DHCP_RELEASE) {
701 : 8 : r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
702 : : SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
703 : 8 : client->req_opts_size, client->req_opts);
704 [ - + ]: 8 : if (r < 0)
705 : 0 : return r;
706 : : }
707 : :
708 : : /* RFC2131 section 3.5:
709 : : The client SHOULD include the ’maximum DHCP message size’ option to
710 : : let the server know how large the server may make its DHCP messages.
711 : :
712 : : Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
713 : : than the defined default size unless the Maximum Message Size option
714 : : is explicitly set
715 : :
716 : : RFC3442 "Requirements to Avoid Sizing Constraints":
717 : : Because a full routing table can be quite large, the standard 576
718 : : octet maximum size for a DHCP message may be too short to contain
719 : : some legitimate Classless Static Route options. Because of this,
720 : : clients implementing the Classless Static Route option SHOULD send a
721 : : Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
722 : : stack is capable of receiving larger IP datagrams. In this case, the
723 : : client SHOULD set the value of this option to at least the MTU of the
724 : : interface that the client is configuring. The client MAY set the
725 : : value of this option higher, up to the size of the largest UDP packet
726 : : it is prepared to accept. (Note that the value specified in the
727 : : Maximum DHCP Message Size option is the total maximum packet size,
728 : : including IP and UDP headers.)
729 : : */
730 : : /* RFC7844 section 3:
731 : : SHOULD NOT contain any other option. */
732 [ + - + - ]: 8 : if (!client->anonymize && type != DHCP_RELEASE) {
733 : 8 : max_size = htobe16(size);
734 : 8 : r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
735 : : SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
736 : : 2, &max_size);
737 [ - + ]: 8 : if (r < 0)
738 : 0 : return r;
739 : : }
740 : :
741 : 8 : *_optlen = optlen;
742 : 8 : *_optoffset = optoffset;
743 : 8 : *ret = TAKE_PTR(packet);
744 : :
745 : 8 : return 0;
746 : : }
747 : :
748 : 0 : static int client_append_fqdn_option(
749 : : DHCPMessage *message,
750 : : size_t optlen,
751 : : size_t *optoffset,
752 : : const char *fqdn) {
753 : :
754 : : uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
755 : : int r;
756 : :
757 : 0 : buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
758 : : DHCP_FQDN_FLAG_E; /* Canonical wire format */
759 : 0 : buffer[1] = 0; /* RCODE1 (deprecated) */
760 : 0 : buffer[2] = 0; /* RCODE2 (deprecated) */
761 : :
762 : 0 : r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
763 [ # # ]: 0 : if (r > 0)
764 : 0 : r = dhcp_option_append(message, optlen, optoffset, 0,
765 : 0 : SD_DHCP_OPTION_FQDN, 3 + r, buffer);
766 : :
767 : 0 : return r;
768 : : }
769 : :
770 : 8 : static int dhcp_client_send_raw(
771 : : sd_dhcp_client *client,
772 : : DHCPPacket *packet,
773 : : size_t len) {
774 : :
775 : 8 : dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
776 : : INADDR_BROADCAST, DHCP_PORT_SERVER, len);
777 : :
778 : 8 : return dhcp_network_send_raw_socket(client->fd, &client->link,
779 : : packet, len);
780 : : }
781 : :
782 : 8 : static int client_send_discover(sd_dhcp_client *client) {
783 : 8 : _cleanup_free_ DHCPPacket *discover = NULL;
784 : : size_t optoffset, optlen;
785 : : int r;
786 : :
787 [ - + ]: 8 : assert(client);
788 [ + - - + ]: 8 : assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
789 : :
790 : 8 : r = client_message_init(client, &discover, DHCP_DISCOVER,
791 : : &optlen, &optoffset);
792 [ - + ]: 8 : if (r < 0)
793 : 0 : return r;
794 : :
795 : : /* the client may suggest values for the network address
796 : : and lease time in the DHCPDISCOVER message. The client may include
797 : : the ’requested IP address’ option to suggest that a particular IP
798 : : address be assigned, and may include the ’IP address lease time’
799 : : option to suggest the lease time it would like.
800 : : */
801 [ - + ]: 8 : if (client->last_addr != INADDR_ANY) {
802 : 0 : r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
803 : : SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
804 : 0 : 4, &client->last_addr);
805 [ # # ]: 0 : if (r < 0)
806 : 0 : return r;
807 : : }
808 : :
809 [ - + ]: 8 : if (client->hostname) {
810 : : /* According to RFC 4702 "clients that send the Client FQDN option in
811 : : their messages MUST NOT also send the Host Name option". Just send
812 : : one of the two depending on the hostname type.
813 : : */
814 [ # # ]: 0 : if (dns_name_is_single_label(client->hostname)) {
815 : : /* it is unclear from RFC 2131 if client should send hostname in
816 : : DHCPDISCOVER but dhclient does and so we do as well
817 : : */
818 : 0 : r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
819 : : SD_DHCP_OPTION_HOST_NAME,
820 : 0 : strlen(client->hostname), client->hostname);
821 : : } else
822 : 0 : r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
823 : 0 : client->hostname);
824 [ # # ]: 0 : if (r < 0)
825 : 0 : return r;
826 : : }
827 : :
828 [ - + ]: 8 : if (client->vendor_class_identifier) {
829 : 0 : r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
830 : : SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
831 : 0 : strlen(client->vendor_class_identifier),
832 : 0 : client->vendor_class_identifier);
833 [ # # ]: 0 : if (r < 0)
834 : 0 : return r;
835 : : }
836 : :
837 [ - + ]: 8 : if (client->user_class) {
838 : 0 : r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
839 : : SD_DHCP_OPTION_USER_CLASS,
840 : 0 : strv_length(client->user_class),
841 : 0 : client->user_class);
842 [ # # ]: 0 : if (r < 0)
843 : 0 : return r;
844 : : }
845 : :
846 : 8 : r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
847 : : SD_DHCP_OPTION_END, 0, NULL);
848 [ - + ]: 8 : if (r < 0)
849 : 0 : return r;
850 : :
851 : : /* We currently ignore:
852 : : The client SHOULD wait a random time between one and ten seconds to
853 : : desynchronize the use of DHCP at startup.
854 : : */
855 : 8 : r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
856 [ - + ]: 8 : if (r < 0)
857 : 0 : return r;
858 : :
859 : 8 : log_dhcp_client(client, "DISCOVER");
860 : :
861 : 8 : return 0;
862 : : }
863 : :
864 : 0 : static int client_send_release(sd_dhcp_client *client) {
865 : 0 : _cleanup_free_ DHCPPacket *release = NULL;
866 : : size_t optoffset, optlen;
867 : : int r;
868 : :
869 [ # # ]: 0 : assert(client);
870 [ # # # # ]: 0 : assert(!IN_SET(client->state, DHCP_STATE_STOPPED));
871 : :
872 : 0 : r = client_message_init(client, &release, DHCP_RELEASE,
873 : : &optlen, &optoffset);
874 [ # # ]: 0 : if (r < 0)
875 : 0 : return r;
876 : :
877 : : /* Fill up release IP and MAC */
878 : 0 : release->dhcp.ciaddr = client->lease->address;
879 : 0 : memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
880 : :
881 : 0 : r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
882 : : SD_DHCP_OPTION_END, 0, NULL);
883 [ # # ]: 0 : if (r < 0)
884 : 0 : return r;
885 : :
886 : 0 : r = dhcp_network_send_udp_socket(client->fd,
887 : 0 : client->lease->server_address,
888 : : DHCP_PORT_SERVER,
889 : 0 : &release->dhcp,
890 : : sizeof(DHCPMessage) + optoffset);
891 [ # # ]: 0 : if (r < 0)
892 : 0 : return r;
893 : :
894 : 0 : log_dhcp_client(client, "RELEASE");
895 : :
896 : 0 : return 0;
897 : : }
898 : :
899 : 0 : static int client_send_request(sd_dhcp_client *client) {
900 : 0 : _cleanup_free_ DHCPPacket *request = NULL;
901 : : size_t optoffset, optlen;
902 : : int r;
903 : :
904 [ # # ]: 0 : assert(client);
905 : :
906 : 0 : r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
907 [ # # ]: 0 : if (r < 0)
908 : 0 : return r;
909 : :
910 [ # # # # : 0 : switch (client->state) {
# ]
911 : : /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
912 : : SELECTING should be REQUESTING)
913 : : */
914 : :
915 : 0 : case DHCP_STATE_REQUESTING:
916 : : /* Client inserts the address of the selected server in ’server
917 : : identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
918 : : filled in with the yiaddr value from the chosen DHCPOFFER.
919 : : */
920 : :
921 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
922 : : SD_DHCP_OPTION_SERVER_IDENTIFIER,
923 : 0 : 4, &client->lease->server_address);
924 [ # # ]: 0 : if (r < 0)
925 : 0 : return r;
926 : :
927 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
928 : : SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
929 : 0 : 4, &client->lease->address);
930 [ # # ]: 0 : if (r < 0)
931 : 0 : return r;
932 : :
933 : 0 : break;
934 : :
935 : 0 : case DHCP_STATE_INIT_REBOOT:
936 : : /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
937 : : option MUST be filled in with client’s notion of its previously
938 : : assigned address. ’ciaddr’ MUST be zero.
939 : : */
940 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
941 : : SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
942 : 0 : 4, &client->last_addr);
943 [ # # ]: 0 : if (r < 0)
944 : 0 : return r;
945 : 0 : break;
946 : :
947 : 0 : case DHCP_STATE_RENEWING:
948 : : /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
949 : : option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
950 : : client’s IP address.
951 : : */
952 : :
953 : : case DHCP_STATE_REBINDING:
954 : : /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
955 : : option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
956 : : client’s IP address.
957 : :
958 : : This message MUST be broadcast to the 0xffffffff IP broadcast address.
959 : : */
960 : 0 : request->dhcp.ciaddr = client->lease->address;
961 : :
962 : 0 : break;
963 : :
964 : 0 : case DHCP_STATE_INIT:
965 : : case DHCP_STATE_SELECTING:
966 : : case DHCP_STATE_REBOOTING:
967 : : case DHCP_STATE_BOUND:
968 : : case DHCP_STATE_STOPPED:
969 : 0 : return -EINVAL;
970 : : }
971 : :
972 [ # # ]: 0 : if (client->hostname) {
973 [ # # ]: 0 : if (dns_name_is_single_label(client->hostname))
974 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
975 : : SD_DHCP_OPTION_HOST_NAME,
976 : 0 : strlen(client->hostname), client->hostname);
977 : : else
978 : 0 : r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
979 : 0 : client->hostname);
980 [ # # ]: 0 : if (r < 0)
981 : 0 : return r;
982 : : }
983 : :
984 [ # # ]: 0 : if (client->vendor_class_identifier) {
985 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
986 : : SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
987 : 0 : strlen(client->vendor_class_identifier),
988 : 0 : client->vendor_class_identifier);
989 [ # # ]: 0 : if (r < 0)
990 : 0 : return r;
991 : : }
992 : :
993 : 0 : r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
994 : : SD_DHCP_OPTION_END, 0, NULL);
995 [ # # ]: 0 : if (r < 0)
996 : 0 : return r;
997 : :
998 [ # # ]: 0 : if (client->state == DHCP_STATE_RENEWING) {
999 : 0 : r = dhcp_network_send_udp_socket(client->fd,
1000 : 0 : client->lease->server_address,
1001 : : DHCP_PORT_SERVER,
1002 : 0 : &request->dhcp,
1003 : : sizeof(DHCPMessage) + optoffset);
1004 : : } else {
1005 : 0 : r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
1006 : : }
1007 [ # # ]: 0 : if (r < 0)
1008 : 0 : return r;
1009 : :
1010 [ # # # # : 0 : switch (client->state) {
# ]
1011 : :
1012 : 0 : case DHCP_STATE_REQUESTING:
1013 : 0 : log_dhcp_client(client, "REQUEST (requesting)");
1014 : 0 : break;
1015 : :
1016 : 0 : case DHCP_STATE_INIT_REBOOT:
1017 : 0 : log_dhcp_client(client, "REQUEST (init-reboot)");
1018 : 0 : break;
1019 : :
1020 : 0 : case DHCP_STATE_RENEWING:
1021 : 0 : log_dhcp_client(client, "REQUEST (renewing)");
1022 : 0 : break;
1023 : :
1024 : 0 : case DHCP_STATE_REBINDING:
1025 : 0 : log_dhcp_client(client, "REQUEST (rebinding)");
1026 : 0 : break;
1027 : :
1028 : 0 : default:
1029 : 0 : log_dhcp_client(client, "REQUEST (invalid)");
1030 : 0 : break;
1031 : : }
1032 : :
1033 : 0 : return 0;
1034 : : }
1035 : :
1036 : : static int client_start(sd_dhcp_client *client);
1037 : :
1038 : 8 : static int client_timeout_resend(
1039 : : sd_event_source *s,
1040 : : uint64_t usec,
1041 : : void *userdata) {
1042 : :
1043 : 8 : sd_dhcp_client *client = userdata;
1044 : 16 : DHCP_CLIENT_DONT_DESTROY(client);
1045 : 8 : usec_t next_timeout = 0;
1046 : : uint64_t time_now;
1047 : : uint32_t time_left;
1048 : : int r;
1049 : :
1050 [ - + ]: 8 : assert(s);
1051 [ - + ]: 8 : assert(client);
1052 [ - + ]: 8 : assert(client->event);
1053 : :
1054 : 8 : r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1055 [ - + ]: 8 : if (r < 0)
1056 : 0 : goto error;
1057 : :
1058 [ - - - + : 8 : switch (client->state) {
- - ]
1059 : :
1060 : 0 : case DHCP_STATE_RENEWING:
1061 : :
1062 : 0 : time_left = (client->lease->t2 - client->lease->t1) / 2;
1063 [ # # ]: 0 : if (time_left < 60)
1064 : 0 : time_left = 60;
1065 : :
1066 : 0 : next_timeout = time_now + time_left * USEC_PER_SEC;
1067 : :
1068 : 0 : break;
1069 : :
1070 : 0 : case DHCP_STATE_REBINDING:
1071 : :
1072 : 0 : time_left = (client->lease->lifetime - client->lease->t2) / 2;
1073 [ # # ]: 0 : if (time_left < 60)
1074 : 0 : time_left = 60;
1075 : :
1076 : 0 : next_timeout = time_now + time_left * USEC_PER_SEC;
1077 : 0 : break;
1078 : :
1079 : 0 : case DHCP_STATE_REBOOTING:
1080 : : /* start over as we did not receive a timely ack or nak */
1081 : 0 : r = client_initialize(client);
1082 [ # # ]: 0 : if (r < 0)
1083 : 0 : goto error;
1084 : :
1085 : 0 : r = client_start(client);
1086 [ # # ]: 0 : if (r < 0)
1087 : 0 : goto error;
1088 : : else {
1089 : 0 : log_dhcp_client(client, "REBOOTED");
1090 : 0 : return 0;
1091 : : }
1092 : :
1093 : 8 : case DHCP_STATE_INIT:
1094 : : case DHCP_STATE_INIT_REBOOT:
1095 : : case DHCP_STATE_SELECTING:
1096 : : case DHCP_STATE_REQUESTING:
1097 : : case DHCP_STATE_BOUND:
1098 : :
1099 [ + - ]: 8 : if (client->attempt < client->max_attempts)
1100 : 8 : client->attempt++;
1101 : : else
1102 : 0 : goto error;
1103 : :
1104 : 8 : next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC;
1105 : :
1106 : 8 : break;
1107 : :
1108 : 0 : case DHCP_STATE_STOPPED:
1109 : 0 : r = -EINVAL;
1110 : 0 : goto error;
1111 : : }
1112 : :
1113 : 8 : next_timeout += (random_u32() & 0x1fffff);
1114 : :
1115 : 8 : r = event_reset_time(client->event, &client->timeout_resend,
1116 : : clock_boottime_or_monotonic(),
1117 : : next_timeout, 10 * USEC_PER_MSEC,
1118 : : client_timeout_resend, client,
1119 : 8 : client->event_priority, "dhcp4-resend-timer", true);
1120 [ - + ]: 8 : if (r < 0)
1121 : 0 : goto error;
1122 : :
1123 [ + - - - : 8 : switch (client->state) {
- - ]
1124 : 8 : case DHCP_STATE_INIT:
1125 : 8 : r = client_send_discover(client);
1126 [ + - ]: 8 : if (r >= 0) {
1127 : 8 : client->state = DHCP_STATE_SELECTING;
1128 : 8 : client->attempt = 0;
1129 [ # # ]: 0 : } else if (client->attempt >= client->max_attempts)
1130 : 0 : goto error;
1131 : :
1132 : 8 : break;
1133 : :
1134 : 0 : case DHCP_STATE_SELECTING:
1135 : 0 : r = client_send_discover(client);
1136 [ # # # # ]: 0 : if (r < 0 && client->attempt >= client->max_attempts)
1137 : 0 : goto error;
1138 : :
1139 : 0 : break;
1140 : :
1141 : 0 : case DHCP_STATE_INIT_REBOOT:
1142 : : case DHCP_STATE_REQUESTING:
1143 : : case DHCP_STATE_RENEWING:
1144 : : case DHCP_STATE_REBINDING:
1145 : 0 : r = client_send_request(client);
1146 [ # # # # ]: 0 : if (r < 0 && client->attempt >= client->max_attempts)
1147 : 0 : goto error;
1148 : :
1149 [ # # ]: 0 : if (client->state == DHCP_STATE_INIT_REBOOT)
1150 : 0 : client->state = DHCP_STATE_REBOOTING;
1151 : :
1152 : 0 : client->request_sent = time_now;
1153 : :
1154 : 0 : break;
1155 : :
1156 : 0 : case DHCP_STATE_REBOOTING:
1157 : : case DHCP_STATE_BOUND:
1158 : :
1159 : 0 : break;
1160 : :
1161 : 0 : case DHCP_STATE_STOPPED:
1162 : 0 : r = -EINVAL;
1163 : 0 : goto error;
1164 : : }
1165 : :
1166 : 8 : return 0;
1167 : :
1168 : 0 : error:
1169 : 0 : client_stop(client, r);
1170 : :
1171 : : /* Errors were dealt with when stopping the client, don't spill
1172 : : errors into the event loop handler */
1173 : 0 : return 0;
1174 : : }
1175 : :
1176 : 8 : static int client_initialize_io_events(
1177 : : sd_dhcp_client *client,
1178 : : sd_event_io_handler_t io_callback) {
1179 : :
1180 : : int r;
1181 : :
1182 [ - + ]: 8 : assert(client);
1183 [ - + ]: 8 : assert(client->event);
1184 : :
1185 : 8 : r = sd_event_add_io(client->event, &client->receive_message,
1186 : : client->fd, EPOLLIN, io_callback,
1187 : : client);
1188 [ - + ]: 8 : if (r < 0)
1189 : 0 : goto error;
1190 : :
1191 : 8 : r = sd_event_source_set_priority(client->receive_message,
1192 : 8 : client->event_priority);
1193 [ - + ]: 8 : if (r < 0)
1194 : 0 : goto error;
1195 : :
1196 : 8 : r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
1197 [ + - ]: 8 : if (r < 0)
1198 : 0 : goto error;
1199 : :
1200 : 8 : error:
1201 [ - + ]: 8 : if (r < 0)
1202 : 0 : client_stop(client, r);
1203 : :
1204 : 8 : return 0;
1205 : : }
1206 : :
1207 : 8 : static int client_initialize_time_events(sd_dhcp_client *client) {
1208 : 8 : uint64_t usec = 0;
1209 : : int r;
1210 : :
1211 [ - + ]: 8 : assert(client);
1212 [ - + ]: 8 : assert(client->event);
1213 : :
1214 [ - + ]: 8 : if (client->start_delay) {
1215 [ # # ]: 0 : assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
1216 : 0 : usec += client->start_delay;
1217 : : }
1218 : :
1219 : 8 : r = event_reset_time(client->event, &client->timeout_resend,
1220 : : clock_boottime_or_monotonic(),
1221 : : usec, 0,
1222 : : client_timeout_resend, client,
1223 : 8 : client->event_priority, "dhcp4-resend-timer", true);
1224 [ - + ]: 8 : if (r < 0)
1225 : 0 : client_stop(client, r);
1226 : :
1227 : 8 : return 0;
1228 : :
1229 : : }
1230 : :
1231 : 8 : static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
1232 : 8 : client_initialize_io_events(client, io_callback);
1233 : 8 : client_initialize_time_events(client);
1234 : :
1235 : 8 : return 0;
1236 : : }
1237 : :
1238 : 8 : static int client_start_delayed(sd_dhcp_client *client) {
1239 : : int r;
1240 : :
1241 [ - + - + ]: 8 : assert_return(client, -EINVAL);
1242 [ - + - + ]: 8 : assert_return(client->event, -EINVAL);
1243 [ - + - + ]: 8 : assert_return(client->ifindex > 0, -EINVAL);
1244 [ - + - + ]: 8 : assert_return(client->fd < 0, -EBUSY);
1245 [ - + - + ]: 8 : assert_return(client->xid == 0, -EINVAL);
1246 [ + - - + : 8 : assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
- + ]
1247 : :
1248 : 8 : client->xid = random_u32();
1249 : :
1250 : 24 : r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
1251 : 8 : client->xid, client->mac_addr,
1252 : 8 : client->mac_addr_len, client->arp_type, client->port);
1253 [ - + ]: 8 : if (r < 0) {
1254 : 0 : client_stop(client, r);
1255 : 0 : return r;
1256 : : }
1257 : 8 : client->fd = r;
1258 : :
1259 [ + - + - ]: 8 : if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT))
1260 : 8 : client->start_time = now(clock_boottime_or_monotonic());
1261 : :
1262 : 8 : return client_initialize_events(client, client_receive_message_raw);
1263 : : }
1264 : :
1265 : 8 : static int client_start(sd_dhcp_client *client) {
1266 : 8 : client->start_delay = 0;
1267 : 8 : return client_start_delayed(client);
1268 : : }
1269 : :
1270 : 0 : static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
1271 : 0 : sd_dhcp_client *client = userdata;
1272 : 0 : DHCP_CLIENT_DONT_DESTROY(client);
1273 : :
1274 : 0 : log_dhcp_client(client, "EXPIRED");
1275 : :
1276 : 0 : client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
1277 : :
1278 : : /* lease was lost, start over if not freed or stopped in callback */
1279 [ # # ]: 0 : if (client->state != DHCP_STATE_STOPPED) {
1280 : 0 : client_initialize(client);
1281 : 0 : client_start(client);
1282 : : }
1283 : :
1284 : 0 : return 0;
1285 : : }
1286 : :
1287 : 0 : static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
1288 : 0 : sd_dhcp_client *client = userdata;
1289 : 0 : DHCP_CLIENT_DONT_DESTROY(client);
1290 : : int r;
1291 : :
1292 [ # # ]: 0 : assert(client);
1293 : :
1294 : 0 : client->receive_message = sd_event_source_unref(client->receive_message);
1295 : 0 : client->fd = asynchronous_close(client->fd);
1296 : :
1297 : 0 : client->state = DHCP_STATE_REBINDING;
1298 : 0 : client->attempt = 0;
1299 : :
1300 : 0 : r = dhcp_network_bind_raw_socket(client->ifindex, &client->link,
1301 : 0 : client->xid, client->mac_addr,
1302 : 0 : client->mac_addr_len, client->arp_type,
1303 : 0 : client->port);
1304 [ # # ]: 0 : if (r < 0) {
1305 : 0 : client_stop(client, r);
1306 : 0 : return 0;
1307 : : }
1308 : 0 : client->fd = r;
1309 : :
1310 : 0 : return client_initialize_events(client, client_receive_message_raw);
1311 : : }
1312 : :
1313 : 0 : static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
1314 : 0 : sd_dhcp_client *client = userdata;
1315 : 0 : DHCP_CLIENT_DONT_DESTROY(client);
1316 : :
1317 : 0 : client->state = DHCP_STATE_RENEWING;
1318 : 0 : client->attempt = 0;
1319 : :
1320 : 0 : return client_initialize_time_events(client);
1321 : : }
1322 : :
1323 : 4 : static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) {
1324 : 4 : _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
1325 : : int r;
1326 : :
1327 : 4 : r = dhcp_lease_new(&lease);
1328 [ - + ]: 4 : if (r < 0)
1329 : 0 : return r;
1330 : :
1331 [ + - ]: 4 : if (client->client_id_len) {
1332 : 8 : r = dhcp_lease_set_client_id(lease,
1333 : 4 : (uint8_t *) &client->client_id,
1334 : : client->client_id_len);
1335 [ - + ]: 4 : if (r < 0)
1336 : 0 : return r;
1337 : : }
1338 : :
1339 : 4 : r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
1340 [ - + ]: 4 : if (r != DHCP_OFFER) {
1341 : 0 : log_dhcp_client(client, "received message was not an OFFER, ignoring");
1342 : 0 : return -ENOMSG;
1343 : : }
1344 : :
1345 : 4 : lease->next_server = offer->siaddr;
1346 : 4 : lease->address = offer->yiaddr;
1347 : :
1348 [ + - ]: 4 : if (lease->address == 0 ||
1349 [ + - ]: 4 : lease->server_address == 0 ||
1350 [ - + ]: 4 : lease->lifetime == 0) {
1351 : 0 : log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
1352 : 0 : return -ENOMSG;
1353 : : }
1354 : :
1355 [ - + ]: 4 : if (!lease->have_subnet_mask) {
1356 : 0 : r = dhcp_lease_set_default_subnet_mask(lease);
1357 [ # # ]: 0 : if (r < 0) {
1358 : 0 : log_dhcp_client(client,
1359 : : "received lease lacks subnet mask, "
1360 : : "and a fallback one cannot be generated, ignoring");
1361 : 0 : return -ENOMSG;
1362 : : }
1363 : : }
1364 : :
1365 : 4 : sd_dhcp_lease_unref(client->lease);
1366 : 4 : client->lease = TAKE_PTR(lease);
1367 : :
1368 [ - + ]: 4 : if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
1369 : 0 : return -ENOMSG;
1370 : :
1371 : 4 : log_dhcp_client(client, "OFFER");
1372 : :
1373 : 4 : return 0;
1374 : : }
1375 : :
1376 : 0 : static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
1377 : : int r;
1378 : :
1379 : 0 : r = dhcp_option_parse(force, len, NULL, NULL, NULL);
1380 [ # # ]: 0 : if (r != DHCP_FORCERENEW)
1381 : 0 : return -ENOMSG;
1382 : :
1383 : 0 : log_dhcp_client(client, "FORCERENEW");
1384 : :
1385 : 0 : return 0;
1386 : : }
1387 : :
1388 : 0 : static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
1389 [ # # ]: 0 : if (a->address != b->address)
1390 : 0 : return false;
1391 : :
1392 [ # # ]: 0 : if (a->subnet_mask != b->subnet_mask)
1393 : 0 : return false;
1394 : :
1395 [ # # ]: 0 : if (a->router_size != b->router_size)
1396 : 0 : return false;
1397 : :
1398 [ # # ]: 0 : for (size_t i = 0; i < a->router_size; i++)
1399 [ # # ]: 0 : if (a->router[i].s_addr != b->router[i].s_addr)
1400 : 0 : return false;
1401 : :
1402 : 0 : return true;
1403 : : }
1404 : :
1405 : 0 : static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
1406 : 0 : _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
1407 : 0 : _cleanup_free_ char *error_message = NULL;
1408 : : int r;
1409 : :
1410 : 0 : r = dhcp_lease_new(&lease);
1411 [ # # ]: 0 : if (r < 0)
1412 : 0 : return r;
1413 : :
1414 [ # # ]: 0 : if (client->client_id_len) {
1415 : 0 : r = dhcp_lease_set_client_id(lease,
1416 : 0 : (uint8_t *) &client->client_id,
1417 : : client->client_id_len);
1418 [ # # ]: 0 : if (r < 0)
1419 : 0 : return r;
1420 : : }
1421 : :
1422 : 0 : r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
1423 [ # # ]: 0 : if (r == DHCP_NAK) {
1424 : 0 : log_dhcp_client(client, "NAK: %s", strna(error_message));
1425 : 0 : return -EADDRNOTAVAIL;
1426 : : }
1427 : :
1428 [ # # ]: 0 : if (r != DHCP_ACK) {
1429 : 0 : log_dhcp_client(client, "received message was not an ACK, ignoring");
1430 : 0 : return -ENOMSG;
1431 : : }
1432 : :
1433 : 0 : lease->next_server = ack->siaddr;
1434 : :
1435 : 0 : lease->address = ack->yiaddr;
1436 : :
1437 [ # # ]: 0 : if (lease->address == INADDR_ANY ||
1438 [ # # ]: 0 : lease->server_address == INADDR_ANY ||
1439 [ # # ]: 0 : lease->lifetime == 0) {
1440 : 0 : log_dhcp_client(client, "received lease lacks address, server "
1441 : : "address or lease lifetime, ignoring");
1442 : 0 : return -ENOMSG;
1443 : : }
1444 : :
1445 [ # # ]: 0 : if (lease->subnet_mask == INADDR_ANY) {
1446 : 0 : r = dhcp_lease_set_default_subnet_mask(lease);
1447 [ # # ]: 0 : if (r < 0) {
1448 : 0 : log_dhcp_client(client,
1449 : : "received lease lacks subnet mask, "
1450 : : "and a fallback one cannot be generated, ignoring");
1451 : 0 : return -ENOMSG;
1452 : : }
1453 : : }
1454 : :
1455 : 0 : r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
1456 [ # # ]: 0 : if (client->lease) {
1457 [ # # ]: 0 : if (lease_equal(client->lease, lease))
1458 : 0 : r = SD_DHCP_CLIENT_EVENT_RENEW;
1459 : : else
1460 : 0 : r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
1461 : :
1462 : 0 : client->lease = sd_dhcp_lease_unref(client->lease);
1463 : : }
1464 : :
1465 : 0 : client->lease = TAKE_PTR(lease);
1466 : :
1467 : 0 : log_dhcp_client(client, "ACK");
1468 : :
1469 : 0 : return r;
1470 : : }
1471 : :
1472 : 0 : static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) {
1473 [ # # ]: 0 : assert(client);
1474 [ # # ]: 0 : assert(client->request_sent);
1475 [ # # ]: 0 : assert(lifetime > 0);
1476 : :
1477 [ # # ]: 0 : if (lifetime > 3)
1478 : 0 : lifetime -= 3;
1479 : : else
1480 : 0 : lifetime = 0;
1481 : :
1482 : 0 : return client->request_sent + (lifetime * USEC_PER_SEC * factor) +
1483 : 0 : + (random_u32() & 0x1fffff);
1484 : : }
1485 : :
1486 : 0 : static int client_set_lease_timeouts(sd_dhcp_client *client) {
1487 : : usec_t time_now;
1488 : : uint64_t lifetime_timeout;
1489 : : uint64_t t2_timeout;
1490 : : uint64_t t1_timeout;
1491 : : char time_string[FORMAT_TIMESPAN_MAX];
1492 : : int r;
1493 : :
1494 [ # # ]: 0 : assert(client);
1495 [ # # ]: 0 : assert(client->event);
1496 [ # # ]: 0 : assert(client->lease);
1497 [ # # ]: 0 : assert(client->lease->lifetime);
1498 : :
1499 : : /* don't set timers for infinite leases */
1500 [ # # ]: 0 : if (client->lease->lifetime == 0xffffffff) {
1501 : 0 : (void) event_source_disable(client->timeout_t1);
1502 : 0 : (void) event_source_disable(client->timeout_t2);
1503 : 0 : (void) event_source_disable(client->timeout_expire);
1504 : :
1505 : 0 : return 0;
1506 : : }
1507 : :
1508 : 0 : r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
1509 [ # # ]: 0 : if (r < 0)
1510 : 0 : return r;
1511 [ # # ]: 0 : assert(client->request_sent <= time_now);
1512 : :
1513 : : /* convert the various timeouts from relative (secs) to absolute (usecs) */
1514 : 0 : lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
1515 [ # # # # ]: 0 : if (client->lease->t1 > 0 && client->lease->t2 > 0) {
1516 : : /* both T1 and T2 are given */
1517 [ # # ]: 0 : if (client->lease->t1 < client->lease->t2 &&
1518 [ # # ]: 0 : client->lease->t2 < client->lease->lifetime) {
1519 : : /* they are both valid */
1520 : 0 : t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1521 : 0 : t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1522 : : } else {
1523 : : /* discard both */
1524 : 0 : t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1525 : 0 : client->lease->t2 = (client->lease->lifetime * 7) / 8;
1526 : 0 : t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1527 : 0 : client->lease->t1 = client->lease->lifetime / 2;
1528 : : }
1529 [ # # # # ]: 0 : } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) {
1530 : : /* only T2 is given, and it is valid */
1531 : 0 : t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1532 : 0 : t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1533 : 0 : client->lease->t1 = client->lease->lifetime / 2;
1534 [ # # ]: 0 : if (t2_timeout <= t1_timeout) {
1535 : : /* the computed T1 would be invalid, so discard T2 */
1536 : 0 : t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1537 : 0 : client->lease->t2 = (client->lease->lifetime * 7) / 8;
1538 : : }
1539 [ # # # # ]: 0 : } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) {
1540 : : /* only T1 is given, and it is valid */
1541 : 0 : t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1542 : 0 : t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1543 : 0 : client->lease->t2 = (client->lease->lifetime * 7) / 8;
1544 [ # # ]: 0 : if (t2_timeout <= t1_timeout) {
1545 : : /* the computed T2 would be invalid, so discard T1 */
1546 : 0 : t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1547 : 0 : client->lease->t2 = client->lease->lifetime / 2;
1548 : : }
1549 : : } else {
1550 : : /* fall back to the default timeouts */
1551 : 0 : t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1552 : 0 : client->lease->t1 = client->lease->lifetime / 2;
1553 : 0 : t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1554 : 0 : client->lease->t2 = (client->lease->lifetime * 7) / 8;
1555 : : }
1556 : :
1557 : : /* arm lifetime timeout */
1558 : 0 : r = event_reset_time(client->event, &client->timeout_expire,
1559 : : clock_boottime_or_monotonic(),
1560 : : lifetime_timeout, 10 * USEC_PER_MSEC,
1561 : : client_timeout_expire, client,
1562 : 0 : client->event_priority, "dhcp4-lifetime", true);
1563 [ # # ]: 0 : if (r < 0)
1564 : 0 : return r;
1565 : :
1566 : 0 : log_dhcp_client(client, "lease expires in %s",
1567 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC));
1568 : :
1569 : : /* don't arm earlier timeouts if this has already expired */
1570 [ # # ]: 0 : if (lifetime_timeout <= time_now)
1571 : 0 : return 0;
1572 : :
1573 : : /* arm T2 timeout */
1574 : 0 : r = event_reset_time(client->event, &client->timeout_t2,
1575 : : clock_boottime_or_monotonic(),
1576 : : t2_timeout, 10 * USEC_PER_MSEC,
1577 : : client_timeout_t2, client,
1578 : 0 : client->event_priority, "dhcp4-t2-timeout", true);
1579 [ # # ]: 0 : if (r < 0)
1580 : 0 : return r;
1581 : :
1582 : 0 : log_dhcp_client(client, "T2 expires in %s",
1583 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC));
1584 : :
1585 : : /* don't arm earlier timeout if this has already expired */
1586 [ # # ]: 0 : if (t2_timeout <= time_now)
1587 : 0 : return 0;
1588 : :
1589 : : /* arm T1 timeout */
1590 : 0 : r = event_reset_time(client->event, &client->timeout_t1,
1591 : : clock_boottime_or_monotonic(),
1592 : : t1_timeout, 10 * USEC_PER_MSEC,
1593 : : client_timeout_t1, client,
1594 : 0 : client->event_priority, "dhcp4-t1-timer", true);
1595 [ # # ]: 0 : if (r < 0)
1596 : 0 : return r;
1597 : :
1598 : 0 : log_dhcp_client(client, "T1 expires in %s",
1599 : : format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC));
1600 : :
1601 : 0 : return 0;
1602 : : }
1603 : :
1604 : 4 : static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
1605 : 8 : DHCP_CLIENT_DONT_DESTROY(client);
1606 : : char time_string[FORMAT_TIMESPAN_MAX];
1607 : 4 : int r = 0, notify_event = 0;
1608 : :
1609 [ - + ]: 4 : assert(client);
1610 [ - + ]: 4 : assert(client->event);
1611 [ - + ]: 4 : assert(message);
1612 : :
1613 [ + - - - : 4 : switch (client->state) {
- - ]
1614 : 4 : case DHCP_STATE_SELECTING:
1615 : :
1616 : 4 : r = client_handle_offer(client, message, len);
1617 [ + - ]: 4 : if (r >= 0) {
1618 : :
1619 : 4 : client->state = DHCP_STATE_REQUESTING;
1620 : 4 : client->attempt = 0;
1621 : :
1622 : 4 : r = event_reset_time(client->event, &client->timeout_resend,
1623 : : clock_boottime_or_monotonic(),
1624 : : 0, 0,
1625 : : client_timeout_resend, client,
1626 : 4 : client->event_priority, "dhcp4-resend-timer", true);
1627 [ - + ]: 4 : if (r < 0)
1628 : 0 : goto error;
1629 [ # # ]: 0 : } else if (r == -ENOMSG)
1630 : : /* invalid message, let's ignore it */
1631 : 0 : return 0;
1632 : :
1633 : 4 : break;
1634 : :
1635 : 0 : case DHCP_STATE_REBOOTING:
1636 : : case DHCP_STATE_REQUESTING:
1637 : : case DHCP_STATE_RENEWING:
1638 : : case DHCP_STATE_REBINDING:
1639 : :
1640 : 0 : r = client_handle_ack(client, message, len);
1641 [ # # ]: 0 : if (r >= 0) {
1642 : 0 : client->start_delay = 0;
1643 : 0 : (void) event_source_disable(client->timeout_resend);
1644 : 0 : client->receive_message =
1645 : 0 : sd_event_source_unref(client->receive_message);
1646 : 0 : client->fd = asynchronous_close(client->fd);
1647 : :
1648 [ # # # # ]: 0 : if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1649 : : DHCP_STATE_REBOOTING))
1650 : 0 : notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
1651 [ # # ]: 0 : else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
1652 : 0 : notify_event = r;
1653 : :
1654 : 0 : client->state = DHCP_STATE_BOUND;
1655 : 0 : client->attempt = 0;
1656 : :
1657 : 0 : client->last_addr = client->lease->address;
1658 : :
1659 : 0 : r = client_set_lease_timeouts(client);
1660 [ # # ]: 0 : if (r < 0) {
1661 : 0 : log_dhcp_client(client, "could not set lease timeouts");
1662 : 0 : goto error;
1663 : : }
1664 : :
1665 : 0 : r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port);
1666 [ # # ]: 0 : if (r < 0) {
1667 : 0 : log_dhcp_client(client, "could not bind UDP socket");
1668 : 0 : goto error;
1669 : : }
1670 : :
1671 : 0 : client->fd = r;
1672 : :
1673 : 0 : client_initialize_io_events(client, client_receive_message_udp);
1674 : :
1675 [ # # ]: 0 : if (notify_event) {
1676 : 0 : client_notify(client, notify_event);
1677 [ # # ]: 0 : if (client->state == DHCP_STATE_STOPPED)
1678 : 0 : return 0;
1679 : : }
1680 : :
1681 [ # # ]: 0 : } else if (r == -EADDRNOTAVAIL) {
1682 : : /* got a NAK, let's restart the client */
1683 : 0 : client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
1684 : :
1685 : 0 : r = client_initialize(client);
1686 [ # # ]: 0 : if (r < 0)
1687 : 0 : goto error;
1688 : :
1689 : 0 : r = client_start_delayed(client);
1690 [ # # ]: 0 : if (r < 0)
1691 : 0 : goto error;
1692 : :
1693 : 0 : log_dhcp_client(client, "REBOOT in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1694 : : client->start_delay, USEC_PER_SEC));
1695 : :
1696 [ # # ]: 0 : client->start_delay = CLAMP(client->start_delay * 2,
1697 : : RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
1698 : :
1699 : 0 : return 0;
1700 [ # # ]: 0 : } else if (r == -ENOMSG)
1701 : : /* invalid message, let's ignore it */
1702 : 0 : return 0;
1703 : :
1704 : 0 : break;
1705 : :
1706 : 0 : case DHCP_STATE_BOUND:
1707 : 0 : r = client_handle_forcerenew(client, message, len);
1708 [ # # ]: 0 : if (r >= 0) {
1709 : 0 : r = client_timeout_t1(NULL, 0, client);
1710 [ # # ]: 0 : if (r < 0)
1711 : 0 : goto error;
1712 [ # # ]: 0 : } else if (r == -ENOMSG)
1713 : : /* invalid message, let's ignore it */
1714 : 0 : return 0;
1715 : :
1716 : 0 : break;
1717 : :
1718 : 0 : case DHCP_STATE_INIT:
1719 : : case DHCP_STATE_INIT_REBOOT:
1720 : :
1721 : 0 : break;
1722 : :
1723 : 0 : case DHCP_STATE_STOPPED:
1724 : 0 : r = -EINVAL;
1725 : 0 : goto error;
1726 : : }
1727 : :
1728 : 4 : error:
1729 [ - + ]: 4 : if (r < 0)
1730 : 0 : client_stop(client, r);
1731 : :
1732 : 4 : return r;
1733 : : }
1734 : :
1735 : 0 : static int client_receive_message_udp(
1736 : : sd_event_source *s,
1737 : : int fd,
1738 : : uint32_t revents,
1739 : : void *userdata) {
1740 : :
1741 : 0 : sd_dhcp_client *client = userdata;
1742 : 0 : _cleanup_free_ DHCPMessage *message = NULL;
1743 : 0 : const uint8_t *expected_chaddr = NULL;
1744 : 0 : uint8_t expected_hlen = 0;
1745 : : ssize_t len, buflen;
1746 : :
1747 [ # # ]: 0 : assert(s);
1748 [ # # ]: 0 : assert(client);
1749 : :
1750 : 0 : buflen = next_datagram_size_fd(fd);
1751 [ # # ]: 0 : if (buflen == -ENETDOWN) {
1752 : : /* the link is down. Don't return an error or the I/O event
1753 : : source will be disconnected and we won't be able to receive
1754 : : packets again when the link comes back. */
1755 : 0 : return 0;
1756 : : }
1757 [ # # ]: 0 : if (buflen < 0)
1758 : 0 : return buflen;
1759 : :
1760 : 0 : message = malloc0(buflen);
1761 [ # # ]: 0 : if (!message)
1762 : 0 : return -ENOMEM;
1763 : :
1764 : 0 : len = recv(fd, message, buflen, 0);
1765 [ # # ]: 0 : if (len < 0) {
1766 : : /* see comment above for why we shouldn't error out on ENETDOWN. */
1767 [ # # # # ]: 0 : if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
1768 : 0 : return 0;
1769 : :
1770 : 0 : return log_dhcp_client_errno(client, errno,
1771 : : "Could not receive message from UDP socket: %m");
1772 : : }
1773 [ # # ]: 0 : if ((size_t) len < sizeof(DHCPMessage)) {
1774 : 0 : log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
1775 : 0 : return 0;
1776 : : }
1777 : :
1778 [ # # ]: 0 : if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1779 : 0 : log_dhcp_client(client, "Not a DHCP message: ignoring");
1780 : 0 : return 0;
1781 : : }
1782 : :
1783 [ # # ]: 0 : if (message->op != BOOTREPLY) {
1784 : 0 : log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
1785 : 0 : return 0;
1786 : : }
1787 : :
1788 [ # # ]: 0 : if (message->htype != client->arp_type) {
1789 : 0 : log_dhcp_client(client, "Packet type does not match client type");
1790 : 0 : return 0;
1791 : : }
1792 : :
1793 [ # # ]: 0 : if (client->arp_type == ARPHRD_ETHER) {
1794 : 0 : expected_hlen = ETH_ALEN;
1795 : 0 : expected_chaddr = &client->mac_addr[0];
1796 : : }
1797 : :
1798 [ # # ]: 0 : if (message->hlen != expected_hlen) {
1799 : 0 : log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
1800 : 0 : return 0;
1801 : : }
1802 : :
1803 [ # # # # ]: 0 : if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) {
1804 : 0 : log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
1805 : 0 : return 0;
1806 : : }
1807 : :
1808 [ # # ]: 0 : if (client->state != DHCP_STATE_BOUND &&
1809 [ # # ]: 0 : be32toh(message->xid) != client->xid) {
1810 : : /* in BOUND state, we may receive FORCERENEW with xid set by server,
1811 : : so ignore the xid in this case */
1812 : 0 : log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
1813 : : be32toh(message->xid), client->xid);
1814 : 0 : return 0;
1815 : : }
1816 : :
1817 : 0 : return client_handle_message(client, message, len);
1818 : : }
1819 : :
1820 : 4 : static int client_receive_message_raw(
1821 : : sd_event_source *s,
1822 : : int fd,
1823 : : uint32_t revents,
1824 : : void *userdata) {
1825 : :
1826 : 4 : sd_dhcp_client *client = userdata;
1827 : 4 : _cleanup_free_ DHCPPacket *packet = NULL;
1828 : : uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1829 : 4 : struct iovec iov = {};
1830 : 4 : struct msghdr msg = {
1831 : : .msg_iov = &iov,
1832 : : .msg_iovlen = 1,
1833 : : .msg_control = cmsgbuf,
1834 : : .msg_controllen = sizeof(cmsgbuf),
1835 : : };
1836 : : struct cmsghdr *cmsg;
1837 : 4 : bool checksum = true;
1838 : : ssize_t buflen, len;
1839 : : int r;
1840 : :
1841 [ - + ]: 4 : assert(s);
1842 [ - + ]: 4 : assert(client);
1843 : :
1844 : 4 : buflen = next_datagram_size_fd(fd);
1845 [ - + ]: 4 : if (buflen == -ENETDOWN)
1846 : 0 : return 0;
1847 [ - + ]: 4 : if (buflen < 0)
1848 : 0 : return buflen;
1849 : :
1850 : 4 : packet = malloc0(buflen);
1851 [ - + ]: 4 : if (!packet)
1852 : 0 : return -ENOMEM;
1853 : :
1854 : 4 : iov = IOVEC_MAKE(packet, buflen);
1855 : :
1856 : 4 : len = recvmsg(fd, &msg, 0);
1857 [ - + ]: 4 : if (len < 0) {
1858 [ # # # # ]: 0 : if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
1859 : 0 : return 0;
1860 : :
1861 : 0 : return log_dhcp_client_errno(client, errno,
1862 : : "Could not receive message from raw socket: %m");
1863 [ - + ]: 4 : } else if ((size_t)len < sizeof(DHCPPacket))
1864 : 0 : return 0;
1865 : :
1866 [ - + - + ]: 4 : CMSG_FOREACH(cmsg, &msg)
1867 [ # # ]: 0 : if (cmsg->cmsg_level == SOL_PACKET &&
1868 [ # # ]: 0 : cmsg->cmsg_type == PACKET_AUXDATA &&
1869 [ # # ]: 0 : cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1870 : 0 : struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
1871 : :
1872 : 0 : checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1873 : 0 : break;
1874 : : }
1875 : :
1876 : 4 : r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
1877 [ - + ]: 4 : if (r < 0)
1878 : 0 : return 0;
1879 : :
1880 : 4 : len -= DHCP_IP_UDP_SIZE;
1881 : :
1882 : 4 : return client_handle_message(client, &packet->dhcp, len);
1883 : : }
1884 : :
1885 : 8 : int sd_dhcp_client_start(sd_dhcp_client *client) {
1886 : : int r;
1887 : :
1888 [ - + - + ]: 8 : assert_return(client, -EINVAL);
1889 : :
1890 : 8 : r = client_initialize(client);
1891 [ - + ]: 8 : if (r < 0)
1892 : 0 : return r;
1893 : :
1894 : : /* RFC7844 section 3.3:
1895 : : SHOULD perform a complete four-way handshake, starting with a
1896 : : DHCPDISCOVER, to obtain a new address lease. If the client can
1897 : : ascertain that this is exactly the same network to which it was
1898 : : previously connected, and if the link-layer address did not change,
1899 : : the client MAY issue a DHCPREQUEST to try to reclaim the current
1900 : : address. */
1901 [ - + # # ]: 8 : if (client->last_addr && !client->anonymize)
1902 : 0 : client->state = DHCP_STATE_INIT_REBOOT;
1903 : :
1904 : 8 : r = client_start(client);
1905 [ + - ]: 8 : if (r >= 0)
1906 : 8 : log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
1907 : :
1908 : 8 : return r;
1909 : : }
1910 : :
1911 : 0 : int sd_dhcp_client_send_release(sd_dhcp_client *client) {
1912 [ # # # # ]: 0 : assert_return(client, -EINVAL);
1913 : :
1914 : 0 : client_send_release(client);
1915 : :
1916 : 0 : return 0;
1917 : : }
1918 : :
1919 : 8 : int sd_dhcp_client_stop(sd_dhcp_client *client) {
1920 : 16 : DHCP_CLIENT_DONT_DESTROY(client);
1921 : :
1922 [ - + - + ]: 8 : assert_return(client, -EINVAL);
1923 : :
1924 : 8 : client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
1925 : 8 : client->state = DHCP_STATE_STOPPED;
1926 : :
1927 : 8 : return 0;
1928 : : }
1929 : :
1930 : 16 : int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
1931 : : int r;
1932 : :
1933 [ - + - + ]: 16 : assert_return(client, -EINVAL);
1934 [ - + - + ]: 16 : assert_return(!client->event, -EBUSY);
1935 : :
1936 [ + - ]: 16 : if (event)
1937 : 16 : client->event = sd_event_ref(event);
1938 : : else {
1939 : 0 : r = sd_event_default(&client->event);
1940 [ # # ]: 0 : if (r < 0)
1941 : 0 : return 0;
1942 : : }
1943 : :
1944 : 16 : client->event_priority = priority;
1945 : :
1946 : 16 : return 0;
1947 : : }
1948 : :
1949 : 16 : int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1950 [ - + - + ]: 16 : assert_return(client, -EINVAL);
1951 : :
1952 : 16 : client->event = sd_event_unref(client->event);
1953 : :
1954 : 16 : return 0;
1955 : : }
1956 : :
1957 : 0 : sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1958 [ # # # # ]: 0 : assert_return(client, NULL);
1959 : :
1960 : 0 : return client->event;
1961 : : }
1962 : :
1963 : 16 : static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
1964 [ - + ]: 16 : assert(client);
1965 : :
1966 : 16 : log_dhcp_client(client, "FREE");
1967 : :
1968 : 16 : client->timeout_resend = sd_event_source_unref(client->timeout_resend);
1969 : 16 : client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1970 : 16 : client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1971 : 16 : client->timeout_expire = sd_event_source_unref(client->timeout_expire);
1972 : :
1973 : 16 : client_initialize(client);
1974 : :
1975 : 16 : sd_dhcp_client_detach_event(client);
1976 : :
1977 : 16 : sd_dhcp_lease_unref(client->lease);
1978 : :
1979 : 16 : free(client->req_opts);
1980 : 16 : free(client->hostname);
1981 : 16 : free(client->vendor_class_identifier);
1982 : 16 : client->user_class = strv_free(client->user_class);
1983 : 16 : return mfree(client);
1984 : : }
1985 : :
1986 [ + + - + : 100 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
+ + ]
1987 : :
1988 : 16 : int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
1989 : 16 : _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
1990 : :
1991 [ - + - + ]: 16 : assert_return(ret, -EINVAL);
1992 : :
1993 : 16 : client = new(sd_dhcp_client, 1);
1994 [ - + ]: 16 : if (!client)
1995 : 0 : return -ENOMEM;
1996 : :
1997 : 32 : *client = (sd_dhcp_client) {
1998 : : .n_ref = 1,
1999 : : .state = DHCP_STATE_INIT,
2000 : : .ifindex = -1,
2001 : : .fd = -1,
2002 : : .mtu = DHCP_DEFAULT_MIN_SIZE,
2003 : : .port = DHCP_PORT_CLIENT,
2004 : 16 : .anonymize = !!anonymize,
2005 : : .max_attempts = (uint64_t) -1,
2006 : : };
2007 : : /* NOTE: this could be moved to a function. */
2008 [ + + ]: 16 : if (anonymize) {
2009 : 4 : client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);
2010 : 4 : client->req_opts = memdup(default_req_opts_anonymize, client->req_opts_size);
2011 : : } else {
2012 : 12 : client->req_opts_size = ELEMENTSOF(default_req_opts);
2013 : 12 : client->req_opts = memdup(default_req_opts, client->req_opts_size);
2014 : : }
2015 [ - + ]: 16 : if (!client->req_opts)
2016 : 0 : return -ENOMEM;
2017 : :
2018 : 16 : *ret = TAKE_PTR(client);
2019 : :
2020 : 16 : return 0;
2021 : : }
|