]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-client.c
sd-dhcp-client: fix strict aliasing issue
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-client.c
CommitLineData
011feef8
PF
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <stdlib.h>
21#include <errno.h>
22#include <string.h>
23#include <stdio.h>
46a66b79 24#include <net/ethernet.h>
3b7ca119 25#include <net/if_arp.h>
76253e73 26#include <linux/if_infiniband.h>
5266a81e 27#include <sys/ioctl.h>
011feef8
PF
28
29#include "util.h"
e5b04c8d 30#include "refcnt.h"
22fc2420 31#include "async.h"
011feef8
PF
32
33#include "dhcp-protocol.h"
46a66b79 34#include "dhcp-internal.h"
fe8db0c5 35#include "dhcp-lease-internal.h"
5bac5235 36#include "dhcp-identifier.h"
011feef8
PF
37#include "sd-dhcp-client.h"
38
5bac5235 39#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
76253e73
DW
40#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
41
011feef8 42struct sd_dhcp_client {
e5b04c8d
PF
43 RefCount n_ref;
44
011feef8 45 DHCPState state;
d3d8ac2f 46 sd_event *event;
b25ef18b 47 int event_priority;
d3d8ac2f 48 sd_event_source *timeout_resend;
011feef8 49 int index;
8c00042c
PF
50 int fd;
51 union sockaddr_union link;
52 sd_event_source *receive_message;
f5de5b00 53 bool request_broadcast;
011feef8 54 uint8_t *req_opts;
7a7c74ca 55 size_t req_opts_allocated;
011feef8 56 size_t req_opts_size;
3b349af6 57 be32_t last_addr;
76253e73
DW
58 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
59 size_t mac_addr_len;
60 uint16_t arp_type;
5bac5235
TG
61 struct {
62 uint8_t type;
63 union {
64 struct {
65 /* 0: Generic (non-LL) (RFC 2132) */
66 uint8_t data[MAX_CLIENT_ID_LEN];
67 } _packed_ gen;
68 struct {
69 /* 1: Ethernet Link-Layer (RFC 2132) */
70 uint8_t haddr[ETH_ALEN];
71 } _packed_ eth;
72 struct {
73 /* 2 - 254: ARP/Link-Layer (RFC 2132) */
74 uint8_t haddr[0];
75 } _packed_ ll;
76 struct {
77 /* 255: Node-specific (RFC 4361) */
78 uint32_t iaid;
79 struct duid duid;
80 } _packed_ ns;
81 struct {
82 uint8_t data[MAX_CLIENT_ID_LEN];
83 } _packed_ raw;
84 };
85 } _packed_ client_id;
ba6c0fd6 86 size_t client_id_len;
4cc7a82c 87 char *hostname;
edb85f0d 88 char *vendor_class_identifier;
324f8187 89 uint32_t mtu;
46a66b79 90 uint32_t xid;
d3d8ac2f 91 usec_t start_time;
e2dfc79f 92 unsigned int attempt;
51debc1e
PF
93 usec_t request_sent;
94 sd_event_source *timeout_t1;
95 sd_event_source *timeout_t2;
96 sd_event_source *timeout_expire;
751246ee
PF
97 sd_dhcp_client_cb_t cb;
98 void *userdata;
a6cc569e 99 sd_dhcp_lease *lease;
011feef8
PF
100};
101
102static const uint8_t default_req_opts[] = {
103 DHCP_OPTION_SUBNET_MASK,
104 DHCP_OPTION_ROUTER,
105 DHCP_OPTION_HOST_NAME,
106 DHCP_OPTION_DOMAIN_NAME,
107 DHCP_OPTION_DOMAIN_NAME_SERVER,
108 DHCP_OPTION_NTP_SERVER,
109};
110
e5002702
TG
111static int client_receive_message_raw(sd_event_source *s, int fd,
112 uint32_t revents, void *userdata);
113static int client_receive_message_udp(sd_event_source *s, int fd,
114 uint32_t revents, void *userdata);
574cc928 115static void client_stop(sd_dhcp_client *client, int error);
aba26854 116
751246ee 117int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
5ee482df 118 void *userdata) {
751246ee
PF
119 assert_return(client, -EINVAL);
120
121 client->cb = cb;
122 client->userdata = userdata;
123
124 return 0;
125}
126
f5de5b00
TG
127int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
128 assert_return(client, -EINVAL);
129
130 client->request_broadcast = !!broadcast;
131
132 return 0;
133}
134
5ee482df 135int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
011feef8
PF
136 size_t i;
137
138 assert_return(client, -EINVAL);
781ca7a1
PF
139 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
140 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
141
142 switch(option) {
143 case DHCP_OPTION_PAD:
144 case DHCP_OPTION_OVERLOAD:
145 case DHCP_OPTION_MESSAGE_TYPE:
146 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
147 case DHCP_OPTION_END:
148 return -EINVAL;
149
150 default:
151 break;
152 }
153
154 for (i = 0; i < client->req_opts_size; i++)
155 if (client->req_opts[i] == option)
156 return -EEXIST;
157
7a7c74ca 158 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
011feef8
PF
159 client->req_opts_size + 1))
160 return -ENOMEM;
161
7a7c74ca 162 client->req_opts[client->req_opts_size++] = option;
011feef8
PF
163
164 return 0;
165}
166
167int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
5ee482df 168 const struct in_addr *last_addr) {
011feef8 169 assert_return(client, -EINVAL);
781ca7a1
PF
170 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
171 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
172
173 if (last_addr)
174 client->last_addr = last_addr->s_addr;
175 else
176 client->last_addr = INADDR_ANY;
177
178 return 0;
179}
180
5ee482df 181int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
011feef8 182 assert_return(client, -EINVAL);
781ca7a1
PF
183 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
184 DHCP_STATE_STOPPED), -EBUSY);
d9bf4f8c 185 assert_return(interface_index > 0, -EINVAL);
011feef8
PF
186
187 client->index = interface_index;
188
189 return 0;
190}
191
76253e73
DW
192int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
193 size_t addr_len, uint16_t arp_type) {
574cc928 194 DHCP_CLIENT_DONT_DESTROY(client);
4644fee0
TG
195 bool need_restart = false;
196
46a66b79 197 assert_return(client, -EINVAL);
4644fee0 198 assert_return(addr, -EINVAL);
76253e73
DW
199 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
200 assert_return(arp_type > 0, -EINVAL);
201
202 if (arp_type == ARPHRD_ETHER)
203 assert_return(addr_len == ETH_ALEN, -EINVAL);
204 else if (arp_type == ARPHRD_INFINIBAND)
205 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
206 else
207 return -EINVAL;
46a66b79 208
76253e73
DW
209 if (client->mac_addr_len == addr_len &&
210 memcmp(&client->mac_addr, addr, addr_len) == 0)
4644fee0
TG
211 return 0;
212
781ca7a1 213 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
4644fee0
TG
214 log_dhcp_client(client, "Changing MAC address on running DHCP "
215 "client, restarting");
4644fee0 216 need_restart = true;
574cc928 217 client_stop(client, DHCP_EVENT_STOP);
4644fee0 218 }
02ec5cd7 219
76253e73
DW
220 memcpy(&client->mac_addr, addr, addr_len);
221 client->mac_addr_len = addr_len;
222 client->arp_type = arp_type;
223
ba6c0fd6
DW
224 if (need_restart && client->state != DHCP_STATE_STOPPED)
225 sd_dhcp_client_start(client);
226
227 return 0;
228}
229
230int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
231 const uint8_t **data, size_t *data_len) {
232
233 assert_return(client, -EINVAL);
234 assert_return(type, -EINVAL);
235 assert_return(data, -EINVAL);
236 assert_return(data_len, -EINVAL);
237
238 *type = 0;
239 *data = NULL;
240 *data_len = 0;
241 if (client->client_id_len) {
5bac5235 242 *type = client->client_id.type;
ba6c0fd6 243 *data = client->client_id.raw.data;
5bac5235 244 *data_len = client->client_id_len - sizeof(client->client_id.type);
ba6c0fd6
DW
245 }
246
247 return 0;
248}
249
250int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
251 const uint8_t *data, size_t data_len) {
252 DHCP_CLIENT_DONT_DESTROY(client);
253 bool need_restart = false;
254
255 assert_return(client, -EINVAL);
256 assert_return(data, -EINVAL);
257 assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
258
259 switch (type) {
260 case ARPHRD_ETHER:
261 if (data_len != ETH_ALEN)
262 return -EINVAL;
263 break;
264 case ARPHRD_INFINIBAND:
265 if (data_len != INFINIBAND_ALEN)
266 return -EINVAL;
267 break;
268 default:
269 break;
270 }
271
5bac5235
TG
272 if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
273 client->client_id.type == type &&
ba6c0fd6
DW
274 memcmp(&client->client_id.raw.data, data, data_len) == 0)
275 return 0;
276
277 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
278 log_dhcp_client(client, "Changing client ID on running DHCP "
279 "client, restarting");
280 need_restart = true;
281 client_stop(client, DHCP_EVENT_STOP);
282 }
283
5bac5235 284 client->client_id.type = type;
ba6c0fd6 285 memcpy(&client->client_id.raw.data, data, data_len);
5bac5235 286 client->client_id_len = data_len + sizeof (client->client_id.type);
46a66b79 287
781ca7a1 288 if (need_restart && client->state != DHCP_STATE_STOPPED)
4644fee0
TG
289 sd_dhcp_client_start(client);
290
46a66b79
PF
291 return 0;
292}
293
4cc7a82c
EY
294int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
295 const char *hostname) {
296 char *new_hostname = NULL;
297
298 assert_return(client, -EINVAL);
299
300 if (streq_ptr(client->hostname, hostname))
301 return 0;
302
303 if (hostname) {
304 new_hostname = strdup(hostname);
305 if (!new_hostname)
306 return -ENOMEM;
307 }
308
309 free(client->hostname);
310 client->hostname = new_hostname;
311
312 return 0;
313}
314
edb85f0d
SS
315int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
316 const char *vci) {
317 char *new_vci = NULL;
318
319 assert_return(client, -EINVAL);
320
321 new_vci = strdup(vci);
322 if (!new_vci)
323 return -ENOMEM;
324
325 free(client->vendor_class_identifier);
326
327 client->vendor_class_identifier = new_vci;
328
329 return 0;
330}
331
324f8187
TG
332int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
333 assert_return(client, -EINVAL);
334 assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
335
336 client->mtu = mtu;
337
338 return 0;
339}
340
a6cc569e 341int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
751246ee 342 assert_return(client, -EINVAL);
a6cc569e 343 assert_return(ret, -EINVAL);
751246ee 344
a6cc569e
TG
345 if (client->state != DHCP_STATE_BOUND &&
346 client->state != DHCP_STATE_RENEWING &&
347 client->state != DHCP_STATE_REBINDING)
348 return -EADDRNOTAVAIL;
751246ee 349
a6cc569e 350 *ret = sd_dhcp_lease_ref(client->lease);
4f882b2a
TG
351
352 return 0;
353}
354
574cc928
TG
355static void client_notify(sd_dhcp_client *client, int event) {
356 if (client->cb)
751246ee 357 client->cb(client, event, client->userdata);
3e3d8f78
PF
358}
359
0f941add 360static int client_initialize(sd_dhcp_client *client) {
bbdf06d9 361 assert_return(client, -EINVAL);
bbdf06d9 362
8c00042c
PF
363 client->receive_message =
364 sd_event_source_unref(client->receive_message);
365
22fc2420 366 client->fd = asynchronous_close(client->fd);
8c00042c 367
d3d8ac2f
PF
368 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
369
51debc1e
PF
370 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
371 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
372 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
373
e2dfc79f
PF
374 client->attempt = 1;
375
f8fdefe4 376 client->state = DHCP_STATE_INIT;
0f941add 377 client->xid = 0;
bbdf06d9 378
a6cc569e
TG
379 if (client->lease)
380 client->lease = sd_dhcp_lease_unref(client->lease);
8c00042c 381
0f941add
PF
382 return 0;
383}
384
574cc928
TG
385static void client_stop(sd_dhcp_client *client, int error) {
386 assert(client);
0f941add 387
ccfdc9a1
UTL
388 if (error < 0)
389 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
2d2349cc
TG
390 else if (error == DHCP_EVENT_STOP)
391 log_dhcp_client(client, "STOPPED");
392 else
393 log_dhcp_client(client, "STOPPED: Unknown event");
0f941add 394
574cc928 395 client_notify(client, error);
ee57a737 396
574cc928 397 client_initialize(client);
bbdf06d9
PF
398}
399
424a8732
TG
400static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
401 uint8_t type, size_t *_optlen, size_t *_optoffset) {
402 _cleanup_free_ DHCPPacket *packet;
403 size_t optlen, optoffset, size;
8a9e7616 404 be16_t max_size;
d8d74ef0
DW
405 usec_t time_now;
406 uint16_t secs;
cf597f65 407 int r;
46a66b79 408
6236f49b 409 assert(client);
d8d74ef0 410 assert(client->start_time);
424a8732
TG
411 assert(ret);
412 assert(_optlen);
413 assert(_optoffset);
8a9e7616 414 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
0a1b6da8 415
424a8732
TG
416 optlen = DHCP_MIN_OPTIONS_SIZE;
417 size = sizeof(DHCPPacket) + optlen;
418
419 packet = malloc0(size);
420 if (!packet)
421 return -ENOMEM;
422
423 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
76253e73 424 client->arp_type, optlen, &optoffset);
cf597f65
TG
425 if (r < 0)
426 return r;
46a66b79 427
0a1b6da8
TG
428 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
429 refuse to issue an DHCP lease if 'secs' is set to zero */
d8d74ef0
DW
430 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
431 if (r < 0)
432 return r;
433 assert(time_now >= client->start_time);
434
435 /* seconds between sending first and last DISCOVER
436 * must always be strictly positive to deal with broken servers */
437 secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
438 packet->dhcp.secs = htobe16(secs);
0a1b6da8 439
63a07041
CA
440 /* RFC2132 section 4.1
441 A client that cannot receive unicast IP datagrams until its protocol
442 software has been configured with an IP address SHOULD set the
443 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
444 DHCPREQUEST messages that client sends. The BROADCAST bit will
445 provide a hint to the DHCP server and BOOTP relay agent to broadcast
f5de5b00
TG
446 any messages to the client on the client's subnet.
447
448 Note: some interfaces needs this to be enabled, but some networks
449 needs this to be disabled as broadcasts are filteretd, so this
450 needs to be configurable */
76253e73 451 if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
f5de5b00 452 packet->dhcp.flags = htobe16(0x8000);
63a07041 453
50d6810e
TG
454 /* RFC2132 section 4.1.1:
455 The client MUST include its hardware address in the ’chaddr’ field, if
76253e73
DW
456 necessary for delivery of DHCP reply messages. Non-Ethernet
457 interfaces will leave 'chaddr' empty and use the client identifier
458 instead (eg, RFC 4390 section 2.1).
50d6810e 459 */
76253e73
DW
460 if (client->arp_type == ARPHRD_ETHER)
461 memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
f5a70de7 462
5bac5235
TG
463 /* If no client identifier exists, construct an RFC 4361-compliant one */
464 if (client->client_id_len == 0) {
465 size_t duid_len;
466
467 client->client_id.type = 255;
468
469 r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
470 if (r < 0)
471 return r;
472
473 r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
474 if (r < 0)
475 return r;
476
477 client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
ba6c0fd6
DW
478 }
479
ee57a737 480 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
46a66b79 481 Identifier option is not set */
ba6c0fd6
DW
482 if (client->client_id_len) {
483 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
484 DHCP_OPTION_CLIENT_IDENTIFIER,
485 client->client_id_len,
5bac5235 486 &client->client_id);
ba6c0fd6
DW
487 if (r < 0)
488 return r;
489 }
50d6810e
TG
490
491 /* RFC2131 section 3.5:
492 in its initial DHCPDISCOVER or DHCPREQUEST message, a
493 client may provide the server with a list of specific
494 parameters the client is interested in. If the client
495 includes a list of parameters in a DHCPDISCOVER message,
496 it MUST include that list in any subsequent DHCPREQUEST
497 messages.
498 */
424a8732 499 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
8a9e7616 500 DHCP_OPTION_PARAMETER_REQUEST_LIST,
20b958bf 501 client->req_opts_size, client->req_opts);
8a9e7616
TG
502 if (r < 0)
503 return r;
504
50d6810e
TG
505 /* RFC2131 section 3.5:
506 The client SHOULD include the ’maximum DHCP message size’ option to
507 let the server know how large the server may make its DHCP messages.
508
509 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
510 than the defined default size unless the Maximum Messge Size option
f131770b 511 is explicitly set
324f8187
TG
512
513 RFC3442 "Requirements to Avoid Sizing Constraints":
514 Because a full routing table can be quite large, the standard 576
515 octet maximum size for a DHCP message may be too short to contain
516 some legitimate Classless Static Route options. Because of this,
517 clients implementing the Classless Static Route option SHOULD send a
518 Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
519 stack is capable of receiving larger IP datagrams. In this case, the
520 client SHOULD set the value of this option to at least the MTU of the
521 interface that the client is configuring. The client MAY set the
522 value of this option higher, up to the size of the largest UDP packet
523 it is prepared to accept. (Note that the value specified in the
524 Maximum DHCP Message Size option is the total maximum packet size,
525 including IP and UDP headers.)
50d6810e 526 */
424a8732 527 max_size = htobe16(size);
324f8187 528 r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
8a9e7616
TG
529 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
530 2, &max_size);
531 if (r < 0)
532 return r;
46a66b79 533
424a8732
TG
534 *_optlen = optlen;
535 *_optoffset = optoffset;
536 *ret = packet;
537 packet = NULL;
538
46a66b79
PF
539 return 0;
540}
541
63edaa62
TG
542static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
543 size_t len) {
544 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
545 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
546
547 return dhcp_network_send_raw_socket(client->fd, &client->link,
548 packet, len);
549}
550
6236f49b 551static int client_send_discover(sd_dhcp_client *client) {
9f2a50a3 552 _cleanup_free_ DHCPPacket *discover = NULL;
424a8732 553 size_t optoffset, optlen;
6236f49b
TG
554 int r;
555
556 assert(client);
50d6810e
TG
557 assert(client->state == DHCP_STATE_INIT ||
558 client->state == DHCP_STATE_SELECTING);
559
424a8732
TG
560 r = client_message_init(client, &discover, DHCP_DISCOVER,
561 &optlen, &optoffset);
6236f49b
TG
562 if (r < 0)
563 return r;
46a66b79 564
50d6810e
TG
565 /* the client may suggest values for the network address
566 and lease time in the DHCPDISCOVER message. The client may include
567 the ’requested IP address’ option to suggest that a particular IP
568 address be assigned, and may include the ’IP address lease time’
569 option to suggest the lease time it would like.
570 */
46a66b79 571 if (client->last_addr != INADDR_ANY) {
04b28be1 572 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf
TG
573 DHCP_OPTION_REQUESTED_IP_ADDRESS,
574 4, &client->last_addr);
6236f49b
TG
575 if (r < 0)
576 return r;
46a66b79
PF
577 }
578
4cc7a82c
EY
579 /* it is unclear from RFC 2131 if client should send hostname in
580 DHCPDISCOVER but dhclient does and so we do as well
581 */
582 if (client->hostname) {
583 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
584 DHCP_OPTION_HOST_NAME,
585 strlen(client->hostname), client->hostname);
586 if (r < 0)
587 return r;
588 }
589
edb85f0d
SS
590 if (client->vendor_class_identifier) {
591 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
592 DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
593 strlen(client->vendor_class_identifier),
594 client->vendor_class_identifier);
595 if (r < 0)
596 return r;
597 }
598
04b28be1 599 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf 600 DHCP_OPTION_END, 0, NULL);
f7926298
TA
601 if (r < 0)
602 return r;
46a66b79 603
50d6810e
TG
604 /* We currently ignore:
605 The client SHOULD wait a random time between one and ten seconds to
606 desynchronize the use of DHCP at startup.
607 */
20b958bf 608 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
6236f49b
TG
609 if (r < 0)
610 return r;
e2dfc79f 611
ee57a737
TG
612 log_dhcp_client(client, "DISCOVER");
613
63edaa62 614 return 0;
e2dfc79f
PF
615}
616
6236f49b 617static int client_send_request(sd_dhcp_client *client) {
8186d9dd 618 _cleanup_free_ DHCPPacket *request = NULL;
424a8732 619 size_t optoffset, optlen;
6236f49b 620 int r;
e2dfc79f 621
424a8732
TG
622 r = client_message_init(client, &request, DHCP_REQUEST,
623 &optlen, &optoffset);
6236f49b
TG
624 if (r < 0)
625 return r;
46a66b79 626
8b1243f7 627 switch (client->state) {
50d6810e
TG
628 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
629 SELECTING should be REQUESTING)
630 */
631
632 case DHCP_STATE_REQUESTING:
633 /* Client inserts the address of the selected server in ’server
634 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
635 filled in with the yiaddr value from the chosen DHCPOFFER.
636 */
8b1243f7 637
04b28be1 638 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
639 DHCP_OPTION_SERVER_IDENTIFIER,
640 4, &client->lease->server_address);
6236f49b
TG
641 if (r < 0)
642 return r;
8b1243f7 643
04b28be1 644 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6236f49b
TG
645 DHCP_OPTION_REQUESTED_IP_ADDRESS,
646 4, &client->lease->address);
647 if (r < 0)
648 return r;
649
50d6810e
TG
650 break;
651
652 case DHCP_STATE_INIT_REBOOT:
653 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
654 option MUST be filled in with client’s notion of its previously
655 assigned address. ’ciaddr’ MUST be zero.
656 */
04b28be1 657 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
658 DHCP_OPTION_REQUESTED_IP_ADDRESS,
659 4, &client->last_addr);
6236f49b
TG
660 if (r < 0)
661 return r;
8b1243f7
PF
662 break;
663
8b1243f7 664 case DHCP_STATE_RENEWING:
50d6810e
TG
665 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
666 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
667 client’s IP address.
668 */
669
670 /* fall through */
8b1243f7 671 case DHCP_STATE_REBINDING:
50d6810e
TG
672 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
673 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
674 client’s IP address.
675
676 This message MUST be broadcast to the 0xffffffff IP broadcast address.
677 */
678 request->dhcp.ciaddr = client->lease->address;
8b1243f7
PF
679
680 break;
781ca7a1 681
50d6810e
TG
682 case DHCP_STATE_INIT:
683 case DHCP_STATE_SELECTING:
684 case DHCP_STATE_REBOOTING:
685 case DHCP_STATE_BOUND:
781ca7a1
PF
686 case DHCP_STATE_STOPPED:
687 return -EINVAL;
e2dfc79f 688 }
46a66b79 689
4cc7a82c
EY
690 if (client->hostname) {
691 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
692 DHCP_OPTION_HOST_NAME,
693 strlen(client->hostname), client->hostname);
694 if (r < 0)
695 return r;
696 }
697
04b28be1 698 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
20b958bf 699 DHCP_OPTION_END, 0, NULL);
6236f49b
TG
700 if (r < 0)
701 return r;
46a66b79 702
aba26854 703 if (client->state == DHCP_STATE_RENEWING) {
6236f49b
TG
704 r = dhcp_network_send_udp_socket(client->fd,
705 client->lease->server_address,
706 DHCP_PORT_SERVER,
707 &request->dhcp,
20b958bf 708 sizeof(DHCPMessage) + optoffset);
aba26854 709 } else {
20b958bf 710 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
aba26854 711 }
6236f49b
TG
712 if (r < 0)
713 return r;
46a66b79 714
998d8047
TG
715 switch (client->state) {
716 case DHCP_STATE_REQUESTING:
717 log_dhcp_client(client, "REQUEST (requesting)");
718 break;
719 case DHCP_STATE_INIT_REBOOT:
720 log_dhcp_client(client, "REQUEST (init-reboot)");
721 break;
722 case DHCP_STATE_RENEWING:
723 log_dhcp_client(client, "REQUEST (renewing)");
724 break;
725 case DHCP_STATE_REBINDING:
726 log_dhcp_client(client, "REQUEST (rebinding)");
727 break;
728 default:
729 log_dhcp_client(client, "REQUEST (invalid)");
730 break;
731 }
ee57a737 732
63edaa62 733 return 0;
46a66b79
PF
734}
735
7739a40b
TG
736static int client_start(sd_dhcp_client *client);
737
d3d8ac2f 738static int client_timeout_resend(sd_event_source *s, uint64_t usec,
5ee482df 739 void *userdata) {
d3d8ac2f 740 sd_dhcp_client *client = userdata;
574cc928 741 DHCP_CLIENT_DONT_DESTROY(client);
aba26854 742 usec_t next_timeout = 0;
d23c45bf 743 uint64_t time_now;
aba26854 744 uint32_t time_left;
d23c45bf 745 int r;
b25ef18b
TG
746
747 assert(s);
748 assert(client);
749 assert(client->event);
d3d8ac2f 750
fa94c34b 751 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d23c45bf
TG
752 if (r < 0)
753 goto error;
754
aba26854
PF
755 switch (client->state) {
756 case DHCP_STATE_RENEWING:
757
e5002702 758 time_left = (client->lease->t2 - client->lease->t1) / 2;
aba26854
PF
759 if (time_left < 60)
760 time_left = 60;
d3d8ac2f 761
d23c45bf 762 next_timeout = time_now + time_left * USEC_PER_SEC;
d3d8ac2f 763
aba26854
PF
764 break;
765
3dd71400
PF
766 case DHCP_STATE_REBINDING:
767
e5002702 768 time_left = (client->lease->lifetime - client->lease->t2) / 2;
3dd71400
PF
769 if (time_left < 60)
770 time_left = 60;
771
d23c45bf 772 next_timeout = time_now + time_left * USEC_PER_SEC;
3dd71400
PF
773 break;
774
8b1243f7
PF
775 case DHCP_STATE_REBOOTING:
776 /* start over as we did not receive a timely ack or nak */
7739a40b 777 r = client_initialize(client);
eb105b96
TG
778 if (r < 0)
779 goto error;
8b1243f7 780
998d8047
TG
781 r = client_start(client);
782 if (r < 0)
783 goto error;
784 else {
785 log_dhcp_client(client, "REBOOTED");
786 return 0;
787 }
7739a40b 788
aba26854
PF
789 case DHCP_STATE_INIT:
790 case DHCP_STATE_INIT_REBOOT:
aba26854
PF
791 case DHCP_STATE_SELECTING:
792 case DHCP_STATE_REQUESTING:
793 case DHCP_STATE_BOUND:
aba26854
PF
794
795 if (client->attempt < 64)
796 client->attempt *= 2;
797
d23c45bf 798 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
aba26854
PF
799
800 break;
781ca7a1
PF
801
802 case DHCP_STATE_STOPPED:
803 r = -EINVAL;
804 goto error;
aba26854
PF
805 }
806
9bf3b535 807 next_timeout += (random_u32() & 0x1fffff);
d3d8ac2f 808
2333154a
UTL
809 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
810
6a0f1f6d
LP
811 r = sd_event_add_time(client->event,
812 &client->timeout_resend,
fa94c34b 813 clock_boottime_or_monotonic(),
6a0f1f6d
LP
814 next_timeout, 10 * USEC_PER_MSEC,
815 client_timeout_resend, client);
b25ef18b
TG
816 if (r < 0)
817 goto error;
818
e5002702
TG
819 r = sd_event_source_set_priority(client->timeout_resend,
820 client->event_priority);
b25ef18b 821 if (r < 0)
e2dfc79f 822 goto error;
d3d8ac2f 823
356779df 824 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
825 if (r < 0)
826 goto error;
827
e2dfc79f
PF
828 switch (client->state) {
829 case DHCP_STATE_INIT:
6236f49b 830 r = client_send_discover(client);
b25ef18b 831 if (r >= 0) {
e2dfc79f
PF
832 client->state = DHCP_STATE_SELECTING;
833 client->attempt = 1;
834 } else {
835 if (client->attempt >= 64)
836 goto error;
837 }
838
839 break;
840
841 case DHCP_STATE_SELECTING:
6236f49b 842 r = client_send_discover(client);
b25ef18b 843 if (r < 0 && client->attempt >= 64)
d3d8ac2f
PF
844 goto error;
845
e2dfc79f
PF
846 break;
847
8b1243f7 848 case DHCP_STATE_INIT_REBOOT:
e2dfc79f 849 case DHCP_STATE_REQUESTING:
aba26854 850 case DHCP_STATE_RENEWING:
3dd71400 851 case DHCP_STATE_REBINDING:
6236f49b 852 r = client_send_request(client);
b25ef18b 853 if (r < 0 && client->attempt >= 64)
e2dfc79f 854 goto error;
d3d8ac2f 855
8b1243f7
PF
856 if (client->state == DHCP_STATE_INIT_REBOOT)
857 client->state = DHCP_STATE_REBOOTING;
858
d23c45bf 859 client->request_sent = time_now;
51debc1e 860
d3d8ac2f
PF
861 break;
862
d3d8ac2f 863 case DHCP_STATE_REBOOTING:
d3d8ac2f 864 case DHCP_STATE_BOUND:
d3d8ac2f
PF
865
866 break;
781ca7a1
PF
867
868 case DHCP_STATE_STOPPED:
869 r = -EINVAL;
870 goto error;
d3d8ac2f
PF
871 }
872
873 return 0;
874
875error:
b25ef18b 876 client_stop(client, r);
d3d8ac2f
PF
877
878 /* Errors were dealt with when stopping the client, don't spill
879 errors into the event loop handler */
880 return 0;
881}
882
0af03ba5
TG
883static int client_initialize_io_events(sd_dhcp_client *client,
884 sd_event_io_handler_t io_callback) {
6a1cd41e
PF
885 int r;
886
b25ef18b
TG
887 assert(client);
888 assert(client->event);
889
151b9b96
LP
890 r = sd_event_add_io(client->event, &client->receive_message,
891 client->fd, EPOLLIN, io_callback,
892 client);
6a1cd41e
PF
893 if (r < 0)
894 goto error;
895
e5002702
TG
896 r = sd_event_source_set_priority(client->receive_message,
897 client->event_priority);
b25ef18b
TG
898 if (r < 0)
899 goto error;
900
356779df 901 r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
9021bb9f
TG
902 if (r < 0)
903 goto error;
904
0af03ba5
TG
905error:
906 if (r < 0)
907 client_stop(client, r);
908
909 return 0;
910}
911
912static int client_initialize_time_events(sd_dhcp_client *client) {
913 int r;
914
915 assert(client);
916 assert(client->event);
917
2333154a
UTL
918 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
919
6a0f1f6d
LP
920 r = sd_event_add_time(client->event,
921 &client->timeout_resend,
fa94c34b 922 clock_boottime_or_monotonic(),
6a0f1f6d
LP
923 0, 0,
924 client_timeout_resend, client);
b25ef18b
TG
925 if (r < 0)
926 goto error;
927
e5002702
TG
928 r = sd_event_source_set_priority(client->timeout_resend,
929 client->event_priority);
6a1cd41e 930
356779df 931 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
932 if (r < 0)
933 goto error;
934
6a1cd41e
PF
935error:
936 if (r < 0)
937 client_stop(client, r);
938
939 return 0;
940
941}
942
0af03ba5
TG
943static int client_initialize_events(sd_dhcp_client *client,
944 sd_event_io_handler_t io_callback) {
945 client_initialize_io_events(client, io_callback);
946 client_initialize_time_events(client);
947
948 return 0;
949}
950
0f941add
PF
951static int client_start(sd_dhcp_client *client) {
952 int r;
953
954 assert_return(client, -EINVAL);
955 assert_return(client->event, -EINVAL);
956 assert_return(client->index > 0, -EINVAL);
957 assert_return(client->fd < 0, -EBUSY);
958 assert_return(client->xid == 0, -EINVAL);
959 assert_return(client->state == DHCP_STATE_INIT ||
960 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
961
962 client->xid = random_u32();
963
76253e73
DW
964 r = dhcp_network_bind_raw_socket(client->index, &client->link,
965 client->xid, client->mac_addr,
966 client->mac_addr_len, client->arp_type);
0f941add
PF
967 if (r < 0) {
968 client_stop(client, r);
969 return r;
970 }
0f941add 971 client->fd = r;
58ec2d3e 972
d8d74ef0 973 if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
fa94c34b 974 client->start_time = now(clock_boottime_or_monotonic());
0f941add 975
0f941add
PF
976 return client_initialize_events(client, client_receive_message_raw);
977}
978
51debc1e 979static int client_timeout_expire(sd_event_source *s, uint64_t usec,
5ee482df 980 void *userdata) {
751246ee 981 sd_dhcp_client *client = userdata;
574cc928 982 DHCP_CLIENT_DONT_DESTROY(client);
751246ee 983
ee57a737
TG
984 log_dhcp_client(client, "EXPIRED");
985
574cc928 986 client_notify(client, DHCP_EVENT_EXPIRED);
0f941add 987
781ca7a1 988 /* lease was lost, start over if not freed or stopped in callback */
574cc928 989 if (client->state != DHCP_STATE_STOPPED) {
e5b04c8d
PF
990 client_initialize(client);
991 client_start(client);
992 }
751246ee 993
51debc1e
PF
994 return 0;
995}
996
5ee482df 997static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
3dd71400 998 sd_dhcp_client *client = userdata;
574cc928 999 DHCP_CLIENT_DONT_DESTROY(client);
3dd71400
PF
1000 int r;
1001
03e334a1 1002 client->receive_message = sd_event_source_unref(client->receive_message);
22fc2420 1003 client->fd = asynchronous_close(client->fd);
3dd71400
PF
1004
1005 client->state = DHCP_STATE_REBINDING;
1006 client->attempt = 1;
1007
76253e73
DW
1008 r = dhcp_network_bind_raw_socket(client->index, &client->link,
1009 client->xid, client->mac_addr,
1010 client->mac_addr_len, client->arp_type);
3dd71400
PF
1011 if (r < 0) {
1012 client_stop(client, r);
1013 return 0;
1014 }
3dd71400
PF
1015 client->fd = r;
1016
d23c45bf 1017 return client_initialize_events(client, client_receive_message_raw);
51debc1e
PF
1018}
1019
e5002702
TG
1020static int client_timeout_t1(sd_event_source *s, uint64_t usec,
1021 void *userdata) {
aba26854 1022 sd_dhcp_client *client = userdata;
574cc928 1023 DHCP_CLIENT_DONT_DESTROY(client);
aba26854
PF
1024
1025 client->state = DHCP_STATE_RENEWING;
1026 client->attempt = 1;
1027
0af03ba5 1028 return client_initialize_time_events(client);
51debc1e
PF
1029}
1030
e5002702
TG
1031static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
1032 size_t len) {
a6cc569e 1033 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 1034 int r;
3e3d8f78 1035
a6cc569e
TG
1036 r = dhcp_lease_new(&lease);
1037 if (r < 0)
1038 return r;
8c00042c 1039
e37f74a6
DW
1040 if (client->client_id_len) {
1041 r = dhcp_lease_set_client_id(lease,
5bac5235 1042 (uint8_t *) &client->client_id,
e37f74a6
DW
1043 client->client_id_len);
1044 if (r < 0)
1045 return r;
1046 }
1047
e5002702 1048 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
9e64dd72 1049 if (r != DHCP_OFFER) {
6ff8806e 1050 log_dhcp_client(client, "received message was not an OFFER, ignoring");
5ee482df 1051 return -ENOMSG;
9e64dd72 1052 }
8c00042c 1053
8e34a618
TG
1054 lease->next_server = offer->siaddr;
1055
e5002702 1056 lease->address = offer->yiaddr;
8c00042c
PF
1057
1058 if (lease->address == INADDR_ANY ||
1059 lease->server_address == INADDR_ANY ||
9e64dd72 1060 lease->lifetime == 0) {
6ff8806e 1061 log_dhcp_client(client, "received lease lacks address, server "
9e64dd72 1062 "address or lease lifetime, ignoring");
5ee482df 1063 return -ENOMSG;
9e64dd72
TG
1064 }
1065
1066 if (lease->subnet_mask == INADDR_ANY) {
1067 r = dhcp_lease_set_default_subnet_mask(lease);
1068 if (r < 0) {
6ff8806e 1069 log_dhcp_client(client, "received lease lacks subnet "
9e64dd72
TG
1070 "mask, and a fallback one can not be "
1071 "generated, ignoring");
1072 return -ENOMSG;
1073 }
1074 }
8c00042c 1075
6e00a806 1076 sd_dhcp_lease_unref(client->lease);
8c00042c 1077 client->lease = lease;
5ee482df 1078 lease = NULL;
8c00042c 1079
ee57a737
TG
1080 log_dhcp_client(client, "OFFER");
1081
8c00042c 1082 return 0;
8c00042c
PF
1083}
1084
615c1467
TG
1085static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
1086 size_t len) {
1087 int r;
1088
1089 r = dhcp_option_parse(force, len, NULL, NULL);
1090 if (r != DHCP_FORCERENEW)
1091 return -ENOMSG;
1092
1093 log_dhcp_client(client, "FORCERENEW");
1094
1095 return 0;
1096}
1097
e5002702
TG
1098static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
1099 size_t len) {
a6cc569e 1100 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 1101 int r;
3e3d8f78 1102
a6cc569e
TG
1103 r = dhcp_lease_new(&lease);
1104 if (r < 0)
1105 return r;
3e3d8f78 1106
e37f74a6
DW
1107 if (client->client_id_len) {
1108 r = dhcp_lease_set_client_id(lease,
5bac5235 1109 (uint8_t *) &client->client_id,
e37f74a6
DW
1110 client->client_id_len);
1111 if (r < 0)
1112 return r;
1113 }
1114
e5002702 1115 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
ee57a737
TG
1116 if (r == DHCP_NAK) {
1117 log_dhcp_client(client, "NAK");
2d2349cc 1118 return -EADDRNOTAVAIL;
ee57a737 1119 }
3e3d8f78 1120
9e64dd72 1121 if (r != DHCP_ACK) {
6ff8806e 1122 log_dhcp_client(client, "received message was not an ACK, ignoring");
5ee482df 1123 return -ENOMSG;
9e64dd72 1124 }
3e3d8f78 1125
8e34a618
TG
1126 lease->next_server = ack->siaddr;
1127
e5002702 1128 lease->address = ack->yiaddr;
3e3d8f78
PF
1129
1130 if (lease->address == INADDR_ANY ||
1131 lease->server_address == INADDR_ANY ||
9e64dd72 1132 lease->lifetime == 0) {
6ff8806e 1133 log_dhcp_client(client, "received lease lacks address, server "
9e64dd72 1134 "address or lease lifetime, ignoring");
5ee482df 1135 return -ENOMSG;
9e64dd72
TG
1136 }
1137
1138 if (lease->subnet_mask == INADDR_ANY) {
1139 r = dhcp_lease_set_default_subnet_mask(lease);
1140 if (r < 0) {
6ff8806e 1141 log_dhcp_client(client, "received lease lacks subnet "
9e64dd72
TG
1142 "mask, and a fallback one can not be "
1143 "generated, ignoring");
1144 return -ENOMSG;
1145 }
1146 }
3e3d8f78
PF
1147
1148 r = DHCP_EVENT_IP_ACQUIRE;
1149 if (client->lease) {
1150 if (client->lease->address != lease->address ||
1151 client->lease->subnet_mask != lease->subnet_mask ||
1152 client->lease->router != lease->router) {
1153 r = DHCP_EVENT_IP_CHANGE;
68ceb9df
PF
1154 } else
1155 r = DHCP_EVENT_RENEW;
3e3d8f78 1156
a6cc569e 1157 client->lease = sd_dhcp_lease_unref(client->lease);
3e3d8f78
PF
1158 }
1159
1160 client->lease = lease;
5ee482df 1161 lease = NULL;
3e3d8f78 1162
ee57a737
TG
1163 log_dhcp_client(client, "ACK");
1164
3e3d8f78
PF
1165 return r;
1166}
1167
022446ad
TG
1168static uint64_t client_compute_timeout(sd_dhcp_client *client,
1169 uint32_t lifetime, double factor) {
1170 assert(client);
1171 assert(client->request_sent);
1172 assert(lifetime);
1173
1174 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
9bf3b535 1175 + (random_u32() & 0x1fffff);
51debc1e
PF
1176}
1177
022446ad
TG
1178static int client_set_lease_timeouts(sd_dhcp_client *client) {
1179 usec_t time_now;
1180 uint64_t lifetime_timeout;
1181 uint64_t t2_timeout;
1182 uint64_t t1_timeout;
1183 char time_string[FORMAT_TIMESPAN_MAX];
5ee482df 1184 int r;
51debc1e 1185
b25ef18b
TG
1186 assert(client);
1187 assert(client->event);
022446ad
TG
1188 assert(client->lease);
1189 assert(client->lease->lifetime);
51debc1e 1190
aba26854
PF
1191 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1192 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1193 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
1194
022446ad
TG
1195 /* don't set timers for infinite leases */
1196 if (client->lease->lifetime == 0xffffffff)
1197 return 0;
51debc1e 1198
fa94c34b 1199 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
022446ad
TG
1200 if (r < 0)
1201 return r;
1202 assert(client->request_sent <= time_now);
1203
1204 /* convert the various timeouts from relative (secs) to absolute (usecs) */
1205 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
1206 if (client->lease->t1 && client->lease->t2) {
1207 /* both T1 and T2 are given */
1208 if (client->lease->t1 < client->lease->t2 &&
1209 client->lease->t2 < client->lease->lifetime) {
1210 /* they are both valid */
1211 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1212 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1213 } else {
1214 /* discard both */
1215 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1216 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1217 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1218 client->lease->t1 = client->lease->lifetime / 2;
1219 }
1220 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
1221 /* only T2 is given, and it is valid */
1222 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1223 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1224 client->lease->t1 = client->lease->lifetime / 2;
1225 if (t2_timeout <= t1_timeout) {
1226 /* the computed T1 would be invalid, so discard T2 */
1227 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1228 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1229 }
1230 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
1231 /* only T1 is given, and it is valid */
1232 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1233 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1234 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1235 if (t2_timeout <= t1_timeout) {
1236 /* the computed T2 would be invalid, so discard T1 */
1237 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1238 client->lease->t2 = client->lease->lifetime / 2;
1239 }
1240 } else {
1241 /* fall back to the default timeouts */
1242 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1243 client->lease->t1 = client->lease->lifetime / 2;
1244 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1245 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1246 }
51debc1e 1247
022446ad 1248 /* arm lifetime timeout */
6a0f1f6d 1249 r = sd_event_add_time(client->event, &client->timeout_expire,
fa94c34b 1250 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1251 lifetime_timeout, 10 * USEC_PER_MSEC,
1252 client_timeout_expire, client);
5ee482df
TG
1253 if (r < 0)
1254 return r;
51debc1e 1255
022446ad 1256 r = sd_event_source_set_priority(client->timeout_expire,
e5002702 1257 client->event_priority);
b25ef18b
TG
1258 if (r < 0)
1259 return r;
1260
356779df 1261 r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
9021bb9f
TG
1262 if (r < 0)
1263 return r;
1264
022446ad
TG
1265 log_dhcp_client(client, "lease expires in %s",
1266 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1267 lifetime_timeout - time_now, 0));
51debc1e 1268
022446ad
TG
1269 /* don't arm earlier timeouts if this has already expired */
1270 if (lifetime_timeout <= time_now)
1271 return 0;
51debc1e 1272
022446ad 1273 /* arm T2 timeout */
6a0f1f6d
LP
1274 r = sd_event_add_time(client->event,
1275 &client->timeout_t2,
fa94c34b 1276 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1277 t2_timeout,
1278 10 * USEC_PER_MSEC,
1279 client_timeout_t2, client);
5ee482df
TG
1280 if (r < 0)
1281 return r;
51debc1e 1282
e5002702
TG
1283 r = sd_event_source_set_priority(client->timeout_t2,
1284 client->event_priority);
b25ef18b
TG
1285 if (r < 0)
1286 return r;
1287
356779df 1288 r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
9021bb9f
TG
1289 if (r < 0)
1290 return r;
1291
022446ad
TG
1292 log_dhcp_client(client, "T2 expires in %s",
1293 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1294 t2_timeout - time_now, 0));
1295
1296 /* don't arm earlier timeout if this has already expired */
1297 if (t2_timeout <= time_now)
1298 return 0;
51debc1e 1299
022446ad 1300 /* arm T1 timeout */
6a0f1f6d
LP
1301 r = sd_event_add_time(client->event,
1302 &client->timeout_t1,
fa94c34b 1303 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1304 t1_timeout, 10 * USEC_PER_MSEC,
1305 client_timeout_t1, client);
5ee482df
TG
1306 if (r < 0)
1307 return r;
51debc1e 1308
022446ad 1309 r = sd_event_source_set_priority(client->timeout_t1,
e5002702 1310 client->event_priority);
b25ef18b
TG
1311 if (r < 0)
1312 return r;
1313
356779df 1314 r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
9021bb9f
TG
1315 if (r < 0)
1316 return r;
1317
022446ad
TG
1318 log_dhcp_client(client, "T1 expires in %s",
1319 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1320 t1_timeout - time_now, 0));
1321
51debc1e
PF
1322 return 0;
1323}
1324
e5002702 1325static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
022446ad 1326 int len) {
574cc928 1327 DHCP_CLIENT_DONT_DESTROY(client);
e5002702 1328 int r = 0, notify_event = 0;
8c00042c 1329
b25ef18b
TG
1330 assert(client);
1331 assert(client->event);
e5002702 1332 assert(message);
b25ef18b 1333
8c00042c
PF
1334 switch (client->state) {
1335 case DHCP_STATE_SELECTING:
1336
e5002702
TG
1337 r = client_handle_offer(client, message, len);
1338 if (r >= 0) {
8c00042c 1339
8c00042c
PF
1340 client->timeout_resend =
1341 sd_event_source_unref(client->timeout_resend);
1342
1343 client->state = DHCP_STATE_REQUESTING;
e2dfc79f
PF
1344 client->attempt = 1;
1345
6a0f1f6d
LP
1346 r = sd_event_add_time(client->event,
1347 &client->timeout_resend,
fa94c34b 1348 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1349 0, 0,
1350 client_timeout_resend, client);
3e3d8f78 1351 if (r < 0)
e2dfc79f 1352 goto error;
b25ef18b 1353
e5002702
TG
1354 r = sd_event_source_set_priority(client->timeout_resend,
1355 client->event_priority);
b25ef18b
TG
1356 if (r < 0)
1357 goto error;
9021bb9f 1358
356779df 1359 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
1360 if (r < 0)
1361 goto error;
9e64dd72
TG
1362 } else if (r == -ENOMSG)
1363 /* invalid message, let's ignore it */
1364 return 0;
8c00042c
PF
1365
1366 break;
1367
8b1243f7 1368 case DHCP_STATE_REBOOTING:
3e3d8f78 1369 case DHCP_STATE_REQUESTING:
aba26854 1370 case DHCP_STATE_RENEWING:
3dd71400 1371 case DHCP_STATE_REBINDING:
aba26854 1372
e5002702 1373 r = client_handle_ack(client, message, len);
2d2349cc 1374 if (r >= 0) {
3e3d8f78
PF
1375 client->timeout_resend =
1376 sd_event_source_unref(client->timeout_resend);
affaa94f
DW
1377 client->receive_message =
1378 sd_event_source_unref(client->receive_message);
1379 client->fd = asynchronous_close(client->fd);
3e3d8f78 1380
8b1243f7
PF
1381 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1382 DHCP_STATE_REBOOTING))
aba26854
PF
1383 notify_event = DHCP_EVENT_IP_ACQUIRE;
1384 else if (r != DHCP_EVENT_IP_ACQUIRE)
1385 notify_event = r;
1386
3e3d8f78
PF
1387 client->state = DHCP_STATE_BOUND;
1388 client->attempt = 1;
1389
1390 client->last_addr = client->lease->address;
1391
022446ad 1392 r = client_set_lease_timeouts(client);
0411760a
TG
1393 if (r < 0) {
1394 log_dhcp_client(client, "could not set lease timeouts");
51debc1e 1395 goto error;
0411760a 1396 }
51debc1e 1397
0af03ba5
TG
1398 r = dhcp_network_bind_udp_socket(client->lease->address,
1399 DHCP_PORT_CLIENT);
1400 if (r < 0) {
1401 log_dhcp_client(client, "could not bind UDP socket");
1402 goto error;
1403 }
1404
1405 client->fd = r;
1406
1407 client_initialize_io_events(client, client_receive_message_udp);
1408
e5b04c8d 1409 if (notify_event) {
574cc928
TG
1410 client_notify(client, notify_event);
1411 if (client->state == DHCP_STATE_STOPPED)
e5b04c8d
PF
1412 return 0;
1413 }
3e3d8f78 1414
2d2349cc
TG
1415 } else if (r == -EADDRNOTAVAIL) {
1416 /* got a NAK, let's restart the client */
1417 client->timeout_resend =
1418 sd_event_source_unref(client->timeout_resend);
1419
1420 r = client_initialize(client);
1421 if (r < 0)
1422 goto error;
1423
1424 r = client_start(client);
1425 if (r < 0)
1426 goto error;
1427
1428 log_dhcp_client(client, "REBOOTED");
1429
1430 return 0;
9e64dd72
TG
1431 } else if (r == -ENOMSG)
1432 /* invalid message, let's ignore it */
1433 return 0;
97b9372d 1434
3e3d8f78
PF
1435 break;
1436
615c1467
TG
1437 case DHCP_STATE_BOUND:
1438 r = client_handle_forcerenew(client, message, len);
1439 if (r >= 0) {
1440 r = client_timeout_t1(NULL, 0, client);
1441 if (r < 0)
1442 goto error;
1443 } else if (r == -ENOMSG)
1444 /* invalid message, let's ignore it */
1445 return 0;
1446
1447 break;
1448
8c00042c
PF
1449 case DHCP_STATE_INIT:
1450 case DHCP_STATE_INIT_REBOOT:
8c00042c
PF
1451
1452 break;
781ca7a1
PF
1453
1454 case DHCP_STATE_STOPPED:
1455 r = -EINVAL;
1456 goto error;
8c00042c
PF
1457 }
1458
1459error:
2d2349cc 1460 if (r < 0)
e5b04c8d 1461 client_stop(client, r);
e2dfc79f 1462
e5b04c8d 1463 return r;
8c00042c
PF
1464}
1465
8f61afd8 1466static int client_receive_message_udp(sd_event_source *s, int fd,
e5002702
TG
1467 uint32_t revents, void *userdata) {
1468 sd_dhcp_client *client = userdata;
5266a81e
TG
1469 _cleanup_free_ DHCPMessage *message = NULL;
1470 int buflen = 0, len, r;
76253e73 1471 const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
6ec8e7c7 1472 bool expect_chaddr;
76253e73 1473 uint8_t expected_hlen = 0;
e5002702
TG
1474
1475 assert(s);
1476 assert(client);
e5002702 1477
5266a81e 1478 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1479 if (r < 0)
1480 return r;
1481
1482 if (buflen < 0)
1483 /* this can't be right */
1484 return -EIO;
5266a81e
TG
1485
1486 message = malloc0(buflen);
1487 if (!message)
1488 return -ENOMEM;
1489
1490 len = read(fd, message, buflen);
0c79c68d
TG
1491 if (len < 0) {
1492 log_dhcp_client(client, "could not receive message from UDP "
590b6b91 1493 "socket: %m");
0c79c68d 1494 return 0;
9fbc2523
TG
1495 } else if ((size_t)len < sizeof(DHCPMessage)) {
1496 log_dhcp_client(client, "too small to be a DHCP message: ignoring");
e5002702 1497 return 0;
9fbc2523
TG
1498 }
1499
1500 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1501 log_dhcp_client(client, "not a DHCP message: ignoring");
1502 return 0;
1503 }
1504
1505 if (message->op != BOOTREPLY) {
1506 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1507 return 0;
1508 }
1509
76253e73
DW
1510 if (message->htype != client->arp_type) {
1511 log_dhcp_client(client, "packet type does not match client type");
1512 return 0;
1513 }
1514
1515 if (client->arp_type == ARPHRD_ETHER) {
1516 expected_hlen = ETH_ALEN;
6ec8e7c7 1517 expect_chaddr = true;
76253e73
DW
1518 } else {
1519 /* Non-ethernet links expect zero chaddr */
1520 expected_hlen = 0;
6ec8e7c7 1521 expect_chaddr = false;
76253e73
DW
1522 }
1523
1524 if (message->hlen != expected_hlen) {
1525 log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
9fbc2523
TG
1526 return 0;
1527 }
1528
6ec8e7c7
SL
1529 if (memcmp(&message->chaddr[0], expect_chaddr ?
1530 (void *)&client->mac_addr :
1531 (void *)&zero_mac,
1532 ETH_ALEN)) {
9fbc2523
TG
1533 log_dhcp_client(client, "received chaddr does not match "
1534 "expected: ignoring");
1535 return 0;
1536 }
e5002702 1537
615c1467
TG
1538 if (client->state != DHCP_STATE_BOUND &&
1539 be32toh(message->xid) != client->xid) {
1540 /* in BOUND state, we may receive FORCERENEW with xid set by server,
1541 so ignore the xid in this case */
1542 log_dhcp_client(client, "received xid (%u) does not match "
1543 "expected (%u): ignoring",
1544 be32toh(message->xid), client->xid);
1545 return 0;
1546 }
1547
022446ad 1548 return client_handle_message(client, message, len);
e5002702
TG
1549}
1550
8f61afd8 1551static int client_receive_message_raw(sd_event_source *s, int fd,
e5002702
TG
1552 uint32_t revents, void *userdata) {
1553 sd_dhcp_client *client = userdata;
5266a81e 1554 _cleanup_free_ DHCPPacket *packet = NULL;
55dab2ed
TG
1555 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1556 struct iovec iov = {};
1557 struct msghdr msg = {
1558 .msg_iov = &iov,
1559 .msg_iovlen = 1,
1560 .msg_control = cmsgbuf,
1561 .msg_controllen = sizeof(cmsgbuf),
1562 };
1563 struct cmsghdr *cmsg;
1564 bool checksum = true;
1565 int buflen = 0, len, r;
e5002702
TG
1566
1567 assert(s);
1568 assert(client);
e5002702 1569
5266a81e 1570 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1571 if (r < 0)
1572 return r;
1573
1574 if (buflen < 0)
1575 /* this can't be right */
1576 return -EIO;
5266a81e
TG
1577
1578 packet = malloc0(buflen);
1579 if (!packet)
1580 return -ENOMEM;
1581
55dab2ed
TG
1582 iov.iov_base = packet;
1583 iov.iov_len = buflen;
1584
a38d9945 1585 len = recvmsg(fd, &msg, 0);
55dab2ed
TG
1586 if (len < 0) {
1587 log_dhcp_client(client, "could not receive message from raw "
590b6b91 1588 "socket: %m");
e5002702 1589 return 0;
0c79c68d
TG
1590 } else if ((size_t)len < sizeof(DHCPPacket))
1591 return 0;
55dab2ed
TG
1592
1593 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
48a4612e
TG
1594 if (cmsg->cmsg_level == SOL_PACKET &&
1595 cmsg->cmsg_type == PACKET_AUXDATA &&
1596 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1597 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
55dab2ed
TG
1598
1599 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1600 break;
1601 }
1602 }
e5002702 1603
55dab2ed 1604 r = dhcp_packet_verify_headers(packet, len, checksum);
ac4f16ab 1605 if (r < 0)
9fadd4ca 1606 return 0;
e5002702
TG
1607
1608 len -= DHCP_IP_UDP_SIZE;
1609
022446ad 1610 return client_handle_message(client, &packet->dhcp, len);
e5002702
TG
1611}
1612
5ee482df 1613int sd_dhcp_client_start(sd_dhcp_client *client) {
6a1cd41e 1614 int r;
d3d8ac2f 1615
46a66b79 1616 assert_return(client, -EINVAL);
46a66b79 1617
0f941add
PF
1618 r = client_initialize(client);
1619 if (r < 0)
6a1cd41e 1620 return r;
8c00042c 1621
0f941add
PF
1622 if (client->last_addr)
1623 client->state = DHCP_STATE_INIT_REBOOT;
d3d8ac2f 1624
998d8047
TG
1625 r = client_start(client);
1626 if (r >= 0)
1fa2f38f 1627 log_dhcp_client(client, "STARTED on ifindex %i", client->index);
998d8047
TG
1628
1629 return r;
46a66b79
PF
1630}
1631
5ee482df 1632int sd_dhcp_client_stop(sd_dhcp_client *client) {
574cc928
TG
1633 DHCP_CLIENT_DONT_DESTROY(client);
1634
e5b04c8d
PF
1635 assert_return(client, -EINVAL);
1636
574cc928
TG
1637 client_stop(client, DHCP_EVENT_STOP);
1638 client->state = DHCP_STATE_STOPPED;
e5b04c8d
PF
1639
1640 return 0;
bbdf06d9
PF
1641}
1642
e5002702
TG
1643int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1644 int priority) {
b25ef18b
TG
1645 int r;
1646
1647 assert_return(client, -EINVAL);
1648 assert_return(!client->event, -EBUSY);
1649
1650 if (event)
1651 client->event = sd_event_ref(event);
1652 else {
1653 r = sd_event_default(&client->event);
1654 if (r < 0)
1655 return 0;
1656 }
1657
1658 client->event_priority = priority;
1659
1660 return 0;
1661}
1662
1663int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1664 assert_return(client, -EINVAL);
1665
1666 client->event = sd_event_unref(client->event);
1667
1668 return 0;
1669}
1670
1671sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1672 if (!client)
1673 return NULL;
1674
1675 return client->event;
1676}
1677
e5b04c8d
PF
1678sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1679 if (client)
1680 assert_se(REFCNT_INC(client->n_ref) >= 2);
1681
1682 return client;
1683}
1684
1685sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
f0c4b1c3 1686 if (client && REFCNT_DEC(client->n_ref) == 0) {
574cc928 1687 log_dhcp_client(client, "FREE");
d2fe46b5 1688
e5b04c8d
PF
1689 client_initialize(client);
1690
1691 client->receive_message =
1692 sd_event_source_unref(client->receive_message);
1693
1694 sd_dhcp_client_detach_event(client);
1695
6e00a806
ZJS
1696 sd_dhcp_lease_unref(client->lease);
1697
e5b04c8d 1698 free(client->req_opts);
4cc7a82c 1699 free(client->hostname);
edb85f0d 1700 free(client->vendor_class_identifier);
e5b04c8d 1701 free(client);
e5b04c8d 1702 }
d2fe46b5 1703
574cc928 1704 return NULL;
d2fe46b5
PF
1705}
1706
b25ef18b 1707int sd_dhcp_client_new(sd_dhcp_client **ret) {
574cc928 1708 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
011feef8 1709
b25ef18b 1710 assert_return(ret, -EINVAL);
d3d8ac2f 1711
011feef8
PF
1712 client = new0(sd_dhcp_client, 1);
1713 if (!client)
b25ef18b 1714 return -ENOMEM;
011feef8 1715
e5b04c8d 1716 client->n_ref = REFCNT_INIT;
011feef8
PF
1717 client->state = DHCP_STATE_INIT;
1718 client->index = -1;
8c00042c 1719 client->fd = -1;
e2dfc79f 1720 client->attempt = 1;
324f8187 1721 client->mtu = DHCP_DEFAULT_MIN_SIZE;
011feef8
PF
1722
1723 client->req_opts_size = ELEMENTSOF(default_req_opts);
1724
1725 client->req_opts = memdup(default_req_opts, client->req_opts_size);
b25ef18b
TG
1726 if (!client->req_opts)
1727 return -ENOMEM;
1728
1729 *ret = client;
1730 client = NULL;
011feef8 1731
b25ef18b 1732 return 0;
011feef8 1733}