]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-client.c
util-lib: split our string related calls from util.[ch] into its own file string...
[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
011feef8 20#include <errno.h>
46a66b79 21#include <net/ethernet.h>
3b7ca119 22#include <net/if_arp.h>
07630cea
LP
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
5266a81e 26#include <sys/ioctl.h>
07630cea 27#include <linux/if_infiniband.h>
011feef8 28
07630cea 29#include "sd-dhcp-client.h"
011feef8 30
07630cea
LP
31#include "async.h"
32#include "dhcp-identifier.h"
46a66b79 33#include "dhcp-internal.h"
fe8db0c5 34#include "dhcp-lease-internal.h"
07630cea
LP
35#include "dhcp-protocol.h"
36#include "random-util.h"
37#include "string-util.h"
38#include "util.h"
011feef8 39
5bac5235 40#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
dd168445 41#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
76253e73 42
011feef8 43struct sd_dhcp_client {
3733eec3 44 unsigned n_ref;
e5b04c8d 45
011feef8 46 DHCPState state;
d3d8ac2f 47 sd_event *event;
b25ef18b 48 int event_priority;
d3d8ac2f 49 sd_event_source *timeout_resend;
011feef8 50 int index;
8c00042c
PF
51 int fd;
52 union sockaddr_union link;
53 sd_event_source *receive_message;
f5de5b00 54 bool request_broadcast;
011feef8 55 uint8_t *req_opts;
7a7c74ca 56 size_t req_opts_allocated;
011feef8 57 size_t req_opts_size;
3b349af6 58 be32_t last_addr;
76253e73
DW
59 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
60 size_t mac_addr_len;
61 uint16_t arp_type;
5bac5235
TG
62 struct {
63 uint8_t type;
64 union {
65 struct {
66 /* 0: Generic (non-LL) (RFC 2132) */
67 uint8_t data[MAX_CLIENT_ID_LEN];
68 } _packed_ gen;
69 struct {
70 /* 1: Ethernet Link-Layer (RFC 2132) */
71 uint8_t haddr[ETH_ALEN];
72 } _packed_ eth;
73 struct {
74 /* 2 - 254: ARP/Link-Layer (RFC 2132) */
75 uint8_t haddr[0];
76 } _packed_ ll;
77 struct {
78 /* 255: Node-specific (RFC 4361) */
79 uint32_t iaid;
80 struct duid duid;
81 } _packed_ ns;
82 struct {
83 uint8_t data[MAX_CLIENT_ID_LEN];
84 } _packed_ raw;
85 };
86 } _packed_ client_id;
ba6c0fd6 87 size_t client_id_len;
4cc7a82c 88 char *hostname;
edb85f0d 89 char *vendor_class_identifier;
324f8187 90 uint32_t mtu;
46a66b79 91 uint32_t xid;
d3d8ac2f 92 usec_t start_time;
e2dfc79f 93 unsigned int attempt;
51debc1e
PF
94 usec_t request_sent;
95 sd_event_source *timeout_t1;
96 sd_event_source *timeout_t2;
97 sd_event_source *timeout_expire;
751246ee
PF
98 sd_dhcp_client_cb_t cb;
99 void *userdata;
a6cc569e 100 sd_dhcp_lease *lease;
011feef8
PF
101};
102
103static const uint8_t default_req_opts[] = {
104 DHCP_OPTION_SUBNET_MASK,
105 DHCP_OPTION_ROUTER,
106 DHCP_OPTION_HOST_NAME,
107 DHCP_OPTION_DOMAIN_NAME,
108 DHCP_OPTION_DOMAIN_NAME_SERVER,
011feef8
PF
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;
03748142 217 client_stop(client, SD_DHCP_CLIENT_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;
03748142 281 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
ba6c0fd6
DW
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
e6b18ffa 350 *ret = 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
0339cd77 379 client->lease = sd_dhcp_lease_unref(client->lease);
8c00042c 380
0f941add
PF
381 return 0;
382}
383
574cc928
TG
384static void client_stop(sd_dhcp_client *client, int error) {
385 assert(client);
0f941add 386
ccfdc9a1
UTL
387 if (error < 0)
388 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
03748142 389 else if (error == SD_DHCP_CLIENT_EVENT_STOP)
2d2349cc
TG
390 log_dhcp_client(client, "STOPPED");
391 else
392 log_dhcp_client(client, "STOPPED: Unknown event");
0f941add 393
574cc928 394 client_notify(client, error);
ee57a737 395
574cc928 396 client_initialize(client);
bbdf06d9
PF
397}
398
424a8732
TG
399static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
400 uint8_t type, size_t *_optlen, size_t *_optoffset) {
401 _cleanup_free_ DHCPPacket *packet;
402 size_t optlen, optoffset, size;
8a9e7616 403 be16_t max_size;
d8d74ef0
DW
404 usec_t time_now;
405 uint16_t secs;
cf597f65 406 int r;
46a66b79 407
6236f49b 408 assert(client);
d8d74ef0 409 assert(client->start_time);
424a8732
TG
410 assert(ret);
411 assert(_optlen);
412 assert(_optoffset);
8a9e7616 413 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
0a1b6da8 414
424a8732
TG
415 optlen = DHCP_MIN_OPTIONS_SIZE;
416 size = sizeof(DHCPPacket) + optlen;
417
418 packet = malloc0(size);
419 if (!packet)
420 return -ENOMEM;
421
422 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
76253e73 423 client->arp_type, optlen, &optoffset);
cf597f65
TG
424 if (r < 0)
425 return r;
46a66b79 426
0a1b6da8
TG
427 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
428 refuse to issue an DHCP lease if 'secs' is set to zero */
d8d74ef0
DW
429 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
430 if (r < 0)
431 return r;
432 assert(time_now >= client->start_time);
433
434 /* seconds between sending first and last DISCOVER
435 * must always be strictly positive to deal with broken servers */
436 secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
437 packet->dhcp.secs = htobe16(secs);
0a1b6da8 438
63a07041
CA
439 /* RFC2132 section 4.1
440 A client that cannot receive unicast IP datagrams until its protocol
441 software has been configured with an IP address SHOULD set the
442 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
443 DHCPREQUEST messages that client sends. The BROADCAST bit will
444 provide a hint to the DHCP server and BOOTP relay agent to broadcast
f5de5b00
TG
445 any messages to the client on the client's subnet.
446
447 Note: some interfaces needs this to be enabled, but some networks
448 needs this to be disabled as broadcasts are filteretd, so this
449 needs to be configurable */
76253e73 450 if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
f5de5b00 451 packet->dhcp.flags = htobe16(0x8000);
63a07041 452
50d6810e
TG
453 /* RFC2132 section 4.1.1:
454 The client MUST include its hardware address in the ’chaddr’ field, if
76253e73
DW
455 necessary for delivery of DHCP reply messages. Non-Ethernet
456 interfaces will leave 'chaddr' empty and use the client identifier
457 instead (eg, RFC 4390 section 2.1).
50d6810e 458 */
76253e73
DW
459 if (client->arp_type == ARPHRD_ETHER)
460 memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
f5a70de7 461
5bac5235
TG
462 /* If no client identifier exists, construct an RFC 4361-compliant one */
463 if (client->client_id_len == 0) {
464 size_t duid_len;
465
466 client->client_id.type = 255;
467
468 r = dhcp_identifier_set_iaid(client->index, client->mac_addr, client->mac_addr_len, &client->client_id.ns.iaid);
469 if (r < 0)
470 return r;
471
472 r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
473 if (r < 0)
474 return r;
475
476 client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
ba6c0fd6
DW
477 }
478
ee57a737 479 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
46a66b79 480 Identifier option is not set */
ba6c0fd6
DW
481 if (client->client_id_len) {
482 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
483 DHCP_OPTION_CLIENT_IDENTIFIER,
484 client->client_id_len,
5bac5235 485 &client->client_id);
ba6c0fd6
DW
486 if (r < 0)
487 return r;
488 }
50d6810e
TG
489
490 /* RFC2131 section 3.5:
491 in its initial DHCPDISCOVER or DHCPREQUEST message, a
492 client may provide the server with a list of specific
493 parameters the client is interested in. If the client
494 includes a list of parameters in a DHCPDISCOVER message,
495 it MUST include that list in any subsequent DHCPREQUEST
496 messages.
497 */
424a8732 498 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
8a9e7616 499 DHCP_OPTION_PARAMETER_REQUEST_LIST,
20b958bf 500 client->req_opts_size, client->req_opts);
8a9e7616
TG
501 if (r < 0)
502 return r;
503
50d6810e
TG
504 /* RFC2131 section 3.5:
505 The client SHOULD include the ’maximum DHCP message size’ option to
506 let the server know how large the server may make its DHCP messages.
507
508 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
509 than the defined default size unless the Maximum Messge Size option
f131770b 510 is explicitly set
324f8187
TG
511
512 RFC3442 "Requirements to Avoid Sizing Constraints":
513 Because a full routing table can be quite large, the standard 576
514 octet maximum size for a DHCP message may be too short to contain
515 some legitimate Classless Static Route options. Because of this,
516 clients implementing the Classless Static Route option SHOULD send a
517 Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
518 stack is capable of receiving larger IP datagrams. In this case, the
519 client SHOULD set the value of this option to at least the MTU of the
520 interface that the client is configuring. The client MAY set the
521 value of this option higher, up to the size of the largest UDP packet
522 it is prepared to accept. (Note that the value specified in the
523 Maximum DHCP Message Size option is the total maximum packet size,
524 including IP and UDP headers.)
50d6810e 525 */
424a8732 526 max_size = htobe16(size);
324f8187 527 r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
8a9e7616
TG
528 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
529 2, &max_size);
530 if (r < 0)
531 return r;
46a66b79 532
424a8732
TG
533 *_optlen = optlen;
534 *_optoffset = optoffset;
535 *ret = packet;
536 packet = NULL;
537
46a66b79
PF
538 return 0;
539}
540
63edaa62
TG
541static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
542 size_t len) {
543 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
544 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
545
546 return dhcp_network_send_raw_socket(client->fd, &client->link,
547 packet, len);
548}
549
6236f49b 550static int client_send_discover(sd_dhcp_client *client) {
9f2a50a3 551 _cleanup_free_ DHCPPacket *discover = NULL;
424a8732 552 size_t optoffset, optlen;
6236f49b
TG
553 int r;
554
555 assert(client);
50d6810e
TG
556 assert(client->state == DHCP_STATE_INIT ||
557 client->state == DHCP_STATE_SELECTING);
558
424a8732
TG
559 r = client_message_init(client, &discover, DHCP_DISCOVER,
560 &optlen, &optoffset);
6236f49b
TG
561 if (r < 0)
562 return r;
46a66b79 563
50d6810e
TG
564 /* the client may suggest values for the network address
565 and lease time in the DHCPDISCOVER message. The client may include
566 the ’requested IP address’ option to suggest that a particular IP
567 address be assigned, and may include the ’IP address lease time’
568 option to suggest the lease time it would like.
569 */
46a66b79 570 if (client->last_addr != INADDR_ANY) {
04b28be1 571 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf
TG
572 DHCP_OPTION_REQUESTED_IP_ADDRESS,
573 4, &client->last_addr);
6236f49b
TG
574 if (r < 0)
575 return r;
46a66b79
PF
576 }
577
4cc7a82c
EY
578 /* it is unclear from RFC 2131 if client should send hostname in
579 DHCPDISCOVER but dhclient does and so we do as well
580 */
581 if (client->hostname) {
582 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
583 DHCP_OPTION_HOST_NAME,
584 strlen(client->hostname), client->hostname);
585 if (r < 0)
586 return r;
587 }
588
edb85f0d
SS
589 if (client->vendor_class_identifier) {
590 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
591 DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
592 strlen(client->vendor_class_identifier),
593 client->vendor_class_identifier);
594 if (r < 0)
595 return r;
596 }
597
04b28be1 598 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf 599 DHCP_OPTION_END, 0, NULL);
f7926298
TA
600 if (r < 0)
601 return r;
46a66b79 602
50d6810e
TG
603 /* We currently ignore:
604 The client SHOULD wait a random time between one and ten seconds to
605 desynchronize the use of DHCP at startup.
606 */
20b958bf 607 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
6236f49b
TG
608 if (r < 0)
609 return r;
e2dfc79f 610
ee57a737
TG
611 log_dhcp_client(client, "DISCOVER");
612
63edaa62 613 return 0;
e2dfc79f
PF
614}
615
6236f49b 616static int client_send_request(sd_dhcp_client *client) {
8186d9dd 617 _cleanup_free_ DHCPPacket *request = NULL;
424a8732 618 size_t optoffset, optlen;
6236f49b 619 int r;
e2dfc79f 620
424a8732
TG
621 r = client_message_init(client, &request, DHCP_REQUEST,
622 &optlen, &optoffset);
6236f49b
TG
623 if (r < 0)
624 return r;
46a66b79 625
8b1243f7 626 switch (client->state) {
50d6810e
TG
627 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
628 SELECTING should be REQUESTING)
629 */
630
631 case DHCP_STATE_REQUESTING:
632 /* Client inserts the address of the selected server in ’server
633 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
634 filled in with the yiaddr value from the chosen DHCPOFFER.
635 */
8b1243f7 636
04b28be1 637 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
638 DHCP_OPTION_SERVER_IDENTIFIER,
639 4, &client->lease->server_address);
6236f49b
TG
640 if (r < 0)
641 return r;
8b1243f7 642
04b28be1 643 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6236f49b
TG
644 DHCP_OPTION_REQUESTED_IP_ADDRESS,
645 4, &client->lease->address);
646 if (r < 0)
647 return r;
648
50d6810e
TG
649 break;
650
651 case DHCP_STATE_INIT_REBOOT:
652 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
653 option MUST be filled in with client’s notion of its previously
654 assigned address. ’ciaddr’ MUST be zero.
655 */
04b28be1 656 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
657 DHCP_OPTION_REQUESTED_IP_ADDRESS,
658 4, &client->last_addr);
6236f49b
TG
659 if (r < 0)
660 return r;
8b1243f7
PF
661 break;
662
8b1243f7 663 case DHCP_STATE_RENEWING:
50d6810e
TG
664 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
665 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
666 client’s IP address.
667 */
668
669 /* fall through */
8b1243f7 670 case DHCP_STATE_REBINDING:
50d6810e
TG
671 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
672 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
673 client’s IP address.
674
675 This message MUST be broadcast to the 0xffffffff IP broadcast address.
676 */
677 request->dhcp.ciaddr = client->lease->address;
8b1243f7
PF
678
679 break;
781ca7a1 680
50d6810e
TG
681 case DHCP_STATE_INIT:
682 case DHCP_STATE_SELECTING:
683 case DHCP_STATE_REBOOTING:
684 case DHCP_STATE_BOUND:
781ca7a1
PF
685 case DHCP_STATE_STOPPED:
686 return -EINVAL;
e2dfc79f 687 }
46a66b79 688
4cc7a82c
EY
689 if (client->hostname) {
690 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
691 DHCP_OPTION_HOST_NAME,
692 strlen(client->hostname), client->hostname);
693 if (r < 0)
694 return r;
695 }
696
04b28be1 697 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
20b958bf 698 DHCP_OPTION_END, 0, NULL);
6236f49b
TG
699 if (r < 0)
700 return r;
46a66b79 701
aba26854 702 if (client->state == DHCP_STATE_RENEWING) {
6236f49b
TG
703 r = dhcp_network_send_udp_socket(client->fd,
704 client->lease->server_address,
705 DHCP_PORT_SERVER,
706 &request->dhcp,
20b958bf 707 sizeof(DHCPMessage) + optoffset);
aba26854 708 } else {
20b958bf 709 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
aba26854 710 }
6236f49b
TG
711 if (r < 0)
712 return r;
46a66b79 713
998d8047
TG
714 switch (client->state) {
715 case DHCP_STATE_REQUESTING:
716 log_dhcp_client(client, "REQUEST (requesting)");
717 break;
718 case DHCP_STATE_INIT_REBOOT:
719 log_dhcp_client(client, "REQUEST (init-reboot)");
720 break;
721 case DHCP_STATE_RENEWING:
722 log_dhcp_client(client, "REQUEST (renewing)");
723 break;
724 case DHCP_STATE_REBINDING:
725 log_dhcp_client(client, "REQUEST (rebinding)");
726 break;
727 default:
728 log_dhcp_client(client, "REQUEST (invalid)");
729 break;
730 }
ee57a737 731
63edaa62 732 return 0;
46a66b79
PF
733}
734
7739a40b
TG
735static int client_start(sd_dhcp_client *client);
736
d3d8ac2f 737static int client_timeout_resend(sd_event_source *s, uint64_t usec,
5ee482df 738 void *userdata) {
d3d8ac2f 739 sd_dhcp_client *client = userdata;
574cc928 740 DHCP_CLIENT_DONT_DESTROY(client);
aba26854 741 usec_t next_timeout = 0;
d23c45bf 742 uint64_t time_now;
aba26854 743 uint32_t time_left;
d23c45bf 744 int r;
b25ef18b
TG
745
746 assert(s);
747 assert(client);
748 assert(client->event);
d3d8ac2f 749
fa94c34b 750 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
d23c45bf
TG
751 if (r < 0)
752 goto error;
753
aba26854
PF
754 switch (client->state) {
755 case DHCP_STATE_RENEWING:
756
e5002702 757 time_left = (client->lease->t2 - client->lease->t1) / 2;
aba26854
PF
758 if (time_left < 60)
759 time_left = 60;
d3d8ac2f 760
d23c45bf 761 next_timeout = time_now + time_left * USEC_PER_SEC;
d3d8ac2f 762
aba26854
PF
763 break;
764
3dd71400
PF
765 case DHCP_STATE_REBINDING:
766
e5002702 767 time_left = (client->lease->lifetime - client->lease->t2) / 2;
3dd71400
PF
768 if (time_left < 60)
769 time_left = 60;
770
d23c45bf 771 next_timeout = time_now + time_left * USEC_PER_SEC;
3dd71400
PF
772 break;
773
8b1243f7
PF
774 case DHCP_STATE_REBOOTING:
775 /* start over as we did not receive a timely ack or nak */
7739a40b 776 r = client_initialize(client);
eb105b96
TG
777 if (r < 0)
778 goto error;
8b1243f7 779
998d8047
TG
780 r = client_start(client);
781 if (r < 0)
782 goto error;
783 else {
784 log_dhcp_client(client, "REBOOTED");
785 return 0;
786 }
7739a40b 787
aba26854
PF
788 case DHCP_STATE_INIT:
789 case DHCP_STATE_INIT_REBOOT:
aba26854
PF
790 case DHCP_STATE_SELECTING:
791 case DHCP_STATE_REQUESTING:
792 case DHCP_STATE_BOUND:
aba26854
PF
793
794 if (client->attempt < 64)
795 client->attempt *= 2;
796
d23c45bf 797 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
aba26854
PF
798
799 break;
781ca7a1
PF
800
801 case DHCP_STATE_STOPPED:
802 r = -EINVAL;
803 goto error;
aba26854
PF
804 }
805
9bf3b535 806 next_timeout += (random_u32() & 0x1fffff);
d3d8ac2f 807
2333154a
UTL
808 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
809
6a0f1f6d
LP
810 r = sd_event_add_time(client->event,
811 &client->timeout_resend,
fa94c34b 812 clock_boottime_or_monotonic(),
6a0f1f6d
LP
813 next_timeout, 10 * USEC_PER_MSEC,
814 client_timeout_resend, client);
b25ef18b
TG
815 if (r < 0)
816 goto error;
817
e5002702
TG
818 r = sd_event_source_set_priority(client->timeout_resend,
819 client->event_priority);
b25ef18b 820 if (r < 0)
e2dfc79f 821 goto error;
d3d8ac2f 822
356779df 823 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
824 if (r < 0)
825 goto error;
826
e2dfc79f
PF
827 switch (client->state) {
828 case DHCP_STATE_INIT:
6236f49b 829 r = client_send_discover(client);
b25ef18b 830 if (r >= 0) {
e2dfc79f
PF
831 client->state = DHCP_STATE_SELECTING;
832 client->attempt = 1;
833 } else {
834 if (client->attempt >= 64)
835 goto error;
836 }
837
838 break;
839
840 case DHCP_STATE_SELECTING:
6236f49b 841 r = client_send_discover(client);
b25ef18b 842 if (r < 0 && client->attempt >= 64)
d3d8ac2f
PF
843 goto error;
844
e2dfc79f
PF
845 break;
846
8b1243f7 847 case DHCP_STATE_INIT_REBOOT:
e2dfc79f 848 case DHCP_STATE_REQUESTING:
aba26854 849 case DHCP_STATE_RENEWING:
3dd71400 850 case DHCP_STATE_REBINDING:
6236f49b 851 r = client_send_request(client);
b25ef18b 852 if (r < 0 && client->attempt >= 64)
e2dfc79f 853 goto error;
d3d8ac2f 854
8b1243f7
PF
855 if (client->state == DHCP_STATE_INIT_REBOOT)
856 client->state = DHCP_STATE_REBOOTING;
857
d23c45bf 858 client->request_sent = time_now;
51debc1e 859
d3d8ac2f
PF
860 break;
861
d3d8ac2f 862 case DHCP_STATE_REBOOTING:
d3d8ac2f 863 case DHCP_STATE_BOUND:
d3d8ac2f
PF
864
865 break;
781ca7a1
PF
866
867 case DHCP_STATE_STOPPED:
868 r = -EINVAL;
869 goto error;
d3d8ac2f
PF
870 }
871
872 return 0;
873
874error:
b25ef18b 875 client_stop(client, r);
d3d8ac2f
PF
876
877 /* Errors were dealt with when stopping the client, don't spill
878 errors into the event loop handler */
879 return 0;
880}
881
0af03ba5
TG
882static int client_initialize_io_events(sd_dhcp_client *client,
883 sd_event_io_handler_t io_callback) {
6a1cd41e
PF
884 int r;
885
b25ef18b
TG
886 assert(client);
887 assert(client->event);
888
151b9b96
LP
889 r = sd_event_add_io(client->event, &client->receive_message,
890 client->fd, EPOLLIN, io_callback,
891 client);
6a1cd41e
PF
892 if (r < 0)
893 goto error;
894
e5002702
TG
895 r = sd_event_source_set_priority(client->receive_message,
896 client->event_priority);
b25ef18b
TG
897 if (r < 0)
898 goto error;
899
356779df 900 r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
9021bb9f
TG
901 if (r < 0)
902 goto error;
903
0af03ba5
TG
904error:
905 if (r < 0)
906 client_stop(client, r);
907
908 return 0;
909}
910
911static int client_initialize_time_events(sd_dhcp_client *client) {
912 int r;
913
914 assert(client);
915 assert(client->event);
916
2333154a
UTL
917 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
918
6a0f1f6d
LP
919 r = sd_event_add_time(client->event,
920 &client->timeout_resend,
fa94c34b 921 clock_boottime_or_monotonic(),
6a0f1f6d
LP
922 0, 0,
923 client_timeout_resend, client);
b25ef18b
TG
924 if (r < 0)
925 goto error;
926
e5002702
TG
927 r = sd_event_source_set_priority(client->timeout_resend,
928 client->event_priority);
144c3488
TA
929 if (r < 0)
930 goto error;
6a1cd41e 931
356779df 932 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
933 if (r < 0)
934 goto error;
935
6a1cd41e
PF
936error:
937 if (r < 0)
938 client_stop(client, r);
939
940 return 0;
941
942}
943
0af03ba5
TG
944static int client_initialize_events(sd_dhcp_client *client,
945 sd_event_io_handler_t io_callback) {
946 client_initialize_io_events(client, io_callback);
947 client_initialize_time_events(client);
948
949 return 0;
950}
951
0f941add
PF
952static int client_start(sd_dhcp_client *client) {
953 int r;
954
955 assert_return(client, -EINVAL);
956 assert_return(client->event, -EINVAL);
957 assert_return(client->index > 0, -EINVAL);
958 assert_return(client->fd < 0, -EBUSY);
959 assert_return(client->xid == 0, -EINVAL);
960 assert_return(client->state == DHCP_STATE_INIT ||
961 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
962
963 client->xid = random_u32();
964
76253e73
DW
965 r = dhcp_network_bind_raw_socket(client->index, &client->link,
966 client->xid, client->mac_addr,
967 client->mac_addr_len, client->arp_type);
0f941add
PF
968 if (r < 0) {
969 client_stop(client, r);
970 return r;
971 }
0f941add 972 client->fd = r;
58ec2d3e 973
d8d74ef0 974 if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
fa94c34b 975 client->start_time = now(clock_boottime_or_monotonic());
0f941add 976
0f941add
PF
977 return client_initialize_events(client, client_receive_message_raw);
978}
979
51debc1e 980static int client_timeout_expire(sd_event_source *s, uint64_t usec,
5ee482df 981 void *userdata) {
751246ee 982 sd_dhcp_client *client = userdata;
574cc928 983 DHCP_CLIENT_DONT_DESTROY(client);
751246ee 984
ee57a737
TG
985 log_dhcp_client(client, "EXPIRED");
986
03748142 987 client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
0f941add 988
781ca7a1 989 /* lease was lost, start over if not freed or stopped in callback */
574cc928 990 if (client->state != DHCP_STATE_STOPPED) {
e5b04c8d
PF
991 client_initialize(client);
992 client_start(client);
993 }
751246ee 994
51debc1e
PF
995 return 0;
996}
997
5ee482df 998static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
3dd71400 999 sd_dhcp_client *client = userdata;
574cc928 1000 DHCP_CLIENT_DONT_DESTROY(client);
3dd71400
PF
1001 int r;
1002
03e334a1 1003 client->receive_message = sd_event_source_unref(client->receive_message);
22fc2420 1004 client->fd = asynchronous_close(client->fd);
3dd71400
PF
1005
1006 client->state = DHCP_STATE_REBINDING;
1007 client->attempt = 1;
1008
76253e73
DW
1009 r = dhcp_network_bind_raw_socket(client->index, &client->link,
1010 client->xid, client->mac_addr,
1011 client->mac_addr_len, client->arp_type);
3dd71400
PF
1012 if (r < 0) {
1013 client_stop(client, r);
1014 return 0;
1015 }
3dd71400
PF
1016 client->fd = r;
1017
d23c45bf 1018 return client_initialize_events(client, client_receive_message_raw);
51debc1e
PF
1019}
1020
e5002702
TG
1021static int client_timeout_t1(sd_event_source *s, uint64_t usec,
1022 void *userdata) {
aba26854 1023 sd_dhcp_client *client = userdata;
574cc928 1024 DHCP_CLIENT_DONT_DESTROY(client);
aba26854
PF
1025
1026 client->state = DHCP_STATE_RENEWING;
1027 client->attempt = 1;
1028
0af03ba5 1029 return client_initialize_time_events(client);
51debc1e
PF
1030}
1031
e5002702
TG
1032static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
1033 size_t len) {
a6cc569e 1034 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 1035 int r;
3e3d8f78 1036
a6cc569e
TG
1037 r = dhcp_lease_new(&lease);
1038 if (r < 0)
1039 return r;
8c00042c 1040
e37f74a6
DW
1041 if (client->client_id_len) {
1042 r = dhcp_lease_set_client_id(lease,
5bac5235 1043 (uint8_t *) &client->client_id,
e37f74a6
DW
1044 client->client_id_len);
1045 if (r < 0)
1046 return r;
1047 }
1048
e5002702 1049 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
9e64dd72 1050 if (r != DHCP_OFFER) {
6ff8806e 1051 log_dhcp_client(client, "received message was not an OFFER, ignoring");
5ee482df 1052 return -ENOMSG;
9e64dd72 1053 }
8c00042c 1054
8e34a618 1055 lease->next_server = offer->siaddr;
e5002702 1056 lease->address = offer->yiaddr;
8c00042c 1057
0339cd77
LP
1058 if (lease->address == 0 ||
1059 lease->server_address == 0 ||
9e64dd72 1060 lease->lifetime == 0) {
0339cd77 1061 log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
5ee482df 1062 return -ENOMSG;
9e64dd72
TG
1063 }
1064
0339cd77 1065 if (!lease->have_subnet_mask) {
9e64dd72
TG
1066 r = dhcp_lease_set_default_subnet_mask(lease);
1067 if (r < 0) {
6ff8806e 1068 log_dhcp_client(client, "received lease lacks subnet "
9e64dd72
TG
1069 "mask, and a fallback one can not be "
1070 "generated, ignoring");
1071 return -ENOMSG;
1072 }
1073 }
8c00042c 1074
6e00a806 1075 sd_dhcp_lease_unref(client->lease);
8c00042c 1076 client->lease = lease;
5ee482df 1077 lease = NULL;
8c00042c 1078
ee57a737
TG
1079 log_dhcp_client(client, "OFFER");
1080
8c00042c 1081 return 0;
8c00042c
PF
1082}
1083
615c1467
TG
1084static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
1085 size_t len) {
1086 int r;
1087
1088 r = dhcp_option_parse(force, len, NULL, NULL);
1089 if (r != DHCP_FORCERENEW)
1090 return -ENOMSG;
1091
1092 log_dhcp_client(client, "FORCERENEW");
1093
1094 return 0;
1095}
1096
e5002702
TG
1097static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
1098 size_t len) {
a6cc569e 1099 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 1100 int r;
3e3d8f78 1101
a6cc569e
TG
1102 r = dhcp_lease_new(&lease);
1103 if (r < 0)
1104 return r;
3e3d8f78 1105
e37f74a6
DW
1106 if (client->client_id_len) {
1107 r = dhcp_lease_set_client_id(lease,
5bac5235 1108 (uint8_t *) &client->client_id,
e37f74a6
DW
1109 client->client_id_len);
1110 if (r < 0)
1111 return r;
1112 }
1113
e5002702 1114 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
ee57a737
TG
1115 if (r == DHCP_NAK) {
1116 log_dhcp_client(client, "NAK");
2d2349cc 1117 return -EADDRNOTAVAIL;
ee57a737 1118 }
3e3d8f78 1119
9e64dd72 1120 if (r != DHCP_ACK) {
6ff8806e 1121 log_dhcp_client(client, "received message was not an ACK, ignoring");
5ee482df 1122 return -ENOMSG;
9e64dd72 1123 }
3e3d8f78 1124
8e34a618
TG
1125 lease->next_server = ack->siaddr;
1126
e5002702 1127 lease->address = ack->yiaddr;
3e3d8f78
PF
1128
1129 if (lease->address == INADDR_ANY ||
1130 lease->server_address == INADDR_ANY ||
9e64dd72 1131 lease->lifetime == 0) {
6ff8806e 1132 log_dhcp_client(client, "received lease lacks address, server "
9e64dd72 1133 "address or lease lifetime, ignoring");
5ee482df 1134 return -ENOMSG;
9e64dd72
TG
1135 }
1136
1137 if (lease->subnet_mask == INADDR_ANY) {
1138 r = dhcp_lease_set_default_subnet_mask(lease);
1139 if (r < 0) {
6ff8806e 1140 log_dhcp_client(client, "received lease lacks subnet "
9e64dd72
TG
1141 "mask, and a fallback one can not be "
1142 "generated, ignoring");
1143 return -ENOMSG;
1144 }
1145 }
3e3d8f78 1146
03748142 1147 r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
3e3d8f78
PF
1148 if (client->lease) {
1149 if (client->lease->address != lease->address ||
1150 client->lease->subnet_mask != lease->subnet_mask ||
1151 client->lease->router != lease->router) {
03748142 1152 r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
68ceb9df 1153 } else
03748142 1154 r = SD_DHCP_CLIENT_EVENT_RENEW;
3e3d8f78 1155
a6cc569e 1156 client->lease = sd_dhcp_lease_unref(client->lease);
3e3d8f78
PF
1157 }
1158
1159 client->lease = lease;
5ee482df 1160 lease = NULL;
3e3d8f78 1161
ee57a737
TG
1162 log_dhcp_client(client, "ACK");
1163
3e3d8f78
PF
1164 return r;
1165}
1166
fbcd420a 1167static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) {
022446ad
TG
1168 assert(client);
1169 assert(client->request_sent);
fbcd420a 1170 assert(lifetime > 0);
022446ad 1171
fbcd420a
LP
1172 if (lifetime > 3)
1173 lifetime -= 3;
1174 else
1175 lifetime = 0;
1176
1177 return client->request_sent + (lifetime * USEC_PER_SEC * factor) +
9bf3b535 1178 + (random_u32() & 0x1fffff);
51debc1e
PF
1179}
1180
022446ad
TG
1181static int client_set_lease_timeouts(sd_dhcp_client *client) {
1182 usec_t time_now;
1183 uint64_t lifetime_timeout;
1184 uint64_t t2_timeout;
1185 uint64_t t1_timeout;
1186 char time_string[FORMAT_TIMESPAN_MAX];
5ee482df 1187 int r;
51debc1e 1188
b25ef18b
TG
1189 assert(client);
1190 assert(client->event);
022446ad
TG
1191 assert(client->lease);
1192 assert(client->lease->lifetime);
51debc1e 1193
aba26854
PF
1194 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1195 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1196 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
1197
022446ad
TG
1198 /* don't set timers for infinite leases */
1199 if (client->lease->lifetime == 0xffffffff)
1200 return 0;
51debc1e 1201
fa94c34b 1202 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
022446ad
TG
1203 if (r < 0)
1204 return r;
1205 assert(client->request_sent <= time_now);
1206
1207 /* convert the various timeouts from relative (secs) to absolute (usecs) */
1208 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
fbcd420a 1209 if (client->lease->t1 > 0 && client->lease->t2 > 0) {
022446ad
TG
1210 /* both T1 and T2 are given */
1211 if (client->lease->t1 < client->lease->t2 &&
1212 client->lease->t2 < client->lease->lifetime) {
1213 /* they are both valid */
1214 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1215 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1216 } else {
1217 /* discard both */
1218 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1219 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1220 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1221 client->lease->t1 = client->lease->lifetime / 2;
1222 }
fbcd420a 1223 } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) {
022446ad
TG
1224 /* only T2 is given, and it is valid */
1225 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1226 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1227 client->lease->t1 = client->lease->lifetime / 2;
1228 if (t2_timeout <= t1_timeout) {
1229 /* the computed T1 would be invalid, so discard T2 */
1230 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1231 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1232 }
fbcd420a 1233 } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) {
022446ad
TG
1234 /* only T1 is given, and it is valid */
1235 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1236 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1237 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1238 if (t2_timeout <= t1_timeout) {
1239 /* the computed T2 would be invalid, so discard T1 */
1240 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1241 client->lease->t2 = client->lease->lifetime / 2;
1242 }
1243 } else {
1244 /* fall back to the default timeouts */
1245 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1246 client->lease->t1 = client->lease->lifetime / 2;
1247 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1248 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1249 }
51debc1e 1250
022446ad 1251 /* arm lifetime timeout */
6a0f1f6d 1252 r = sd_event_add_time(client->event, &client->timeout_expire,
fa94c34b 1253 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1254 lifetime_timeout, 10 * USEC_PER_MSEC,
1255 client_timeout_expire, client);
5ee482df
TG
1256 if (r < 0)
1257 return r;
51debc1e 1258
022446ad 1259 r = sd_event_source_set_priority(client->timeout_expire,
e5002702 1260 client->event_priority);
b25ef18b
TG
1261 if (r < 0)
1262 return r;
1263
356779df 1264 r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
9021bb9f
TG
1265 if (r < 0)
1266 return r;
1267
022446ad 1268 log_dhcp_client(client, "lease expires in %s",
ed19c567 1269 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC));
51debc1e 1270
022446ad
TG
1271 /* don't arm earlier timeouts if this has already expired */
1272 if (lifetime_timeout <= time_now)
1273 return 0;
51debc1e 1274
022446ad 1275 /* arm T2 timeout */
6a0f1f6d
LP
1276 r = sd_event_add_time(client->event,
1277 &client->timeout_t2,
fa94c34b 1278 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1279 t2_timeout,
1280 10 * USEC_PER_MSEC,
1281 client_timeout_t2, client);
5ee482df
TG
1282 if (r < 0)
1283 return r;
51debc1e 1284
e5002702
TG
1285 r = sd_event_source_set_priority(client->timeout_t2,
1286 client->event_priority);
b25ef18b
TG
1287 if (r < 0)
1288 return r;
1289
356779df 1290 r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
9021bb9f
TG
1291 if (r < 0)
1292 return r;
1293
022446ad 1294 log_dhcp_client(client, "T2 expires in %s",
ed19c567 1295 format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC));
022446ad
TG
1296
1297 /* don't arm earlier timeout if this has already expired */
1298 if (t2_timeout <= time_now)
1299 return 0;
51debc1e 1300
022446ad 1301 /* arm T1 timeout */
6a0f1f6d
LP
1302 r = sd_event_add_time(client->event,
1303 &client->timeout_t1,
fa94c34b 1304 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1305 t1_timeout, 10 * USEC_PER_MSEC,
1306 client_timeout_t1, client);
5ee482df
TG
1307 if (r < 0)
1308 return r;
51debc1e 1309
022446ad 1310 r = sd_event_source_set_priority(client->timeout_t1,
e5002702 1311 client->event_priority);
b25ef18b
TG
1312 if (r < 0)
1313 return r;
1314
356779df 1315 r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
9021bb9f
TG
1316 if (r < 0)
1317 return r;
1318
022446ad 1319 log_dhcp_client(client, "T1 expires in %s",
ed19c567 1320 format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC));
022446ad 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))
03748142
DH
1383 notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
1384 else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
aba26854
PF
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 } };
3dc05554 1472 const struct ether_addr *expected_chaddr = NULL;
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;
3dc05554 1517 expected_chaddr = (const struct ether_addr *) &client->mac_addr;
76253e73
DW
1518 } else {
1519 /* Non-ethernet links expect zero chaddr */
1520 expected_hlen = 0;
3dc05554 1521 expected_chaddr = &zero_mac;
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
3dc05554 1529 if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
9fbc2523
TG
1530 log_dhcp_client(client, "received chaddr does not match "
1531 "expected: ignoring");
1532 return 0;
1533 }
e5002702 1534
615c1467
TG
1535 if (client->state != DHCP_STATE_BOUND &&
1536 be32toh(message->xid) != client->xid) {
1537 /* in BOUND state, we may receive FORCERENEW with xid set by server,
1538 so ignore the xid in this case */
1539 log_dhcp_client(client, "received xid (%u) does not match "
1540 "expected (%u): ignoring",
1541 be32toh(message->xid), client->xid);
1542 return 0;
1543 }
1544
022446ad 1545 return client_handle_message(client, message, len);
e5002702
TG
1546}
1547
8f61afd8 1548static int client_receive_message_raw(sd_event_source *s, int fd,
e5002702
TG
1549 uint32_t revents, void *userdata) {
1550 sd_dhcp_client *client = userdata;
5266a81e 1551 _cleanup_free_ DHCPPacket *packet = NULL;
55dab2ed
TG
1552 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1553 struct iovec iov = {};
1554 struct msghdr msg = {
1555 .msg_iov = &iov,
1556 .msg_iovlen = 1,
1557 .msg_control = cmsgbuf,
1558 .msg_controllen = sizeof(cmsgbuf),
1559 };
1560 struct cmsghdr *cmsg;
1561 bool checksum = true;
1562 int buflen = 0, len, r;
e5002702
TG
1563
1564 assert(s);
1565 assert(client);
e5002702 1566
5266a81e 1567 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1568 if (r < 0)
1569 return r;
1570
1571 if (buflen < 0)
1572 /* this can't be right */
1573 return -EIO;
5266a81e
TG
1574
1575 packet = malloc0(buflen);
1576 if (!packet)
1577 return -ENOMEM;
1578
55dab2ed
TG
1579 iov.iov_base = packet;
1580 iov.iov_len = buflen;
1581
a38d9945 1582 len = recvmsg(fd, &msg, 0);
55dab2ed
TG
1583 if (len < 0) {
1584 log_dhcp_client(client, "could not receive message from raw "
590b6b91 1585 "socket: %m");
e5002702 1586 return 0;
0c79c68d
TG
1587 } else if ((size_t)len < sizeof(DHCPPacket))
1588 return 0;
55dab2ed 1589
2a1288ff 1590 CMSG_FOREACH(cmsg, &msg) {
48a4612e
TG
1591 if (cmsg->cmsg_level == SOL_PACKET &&
1592 cmsg->cmsg_type == PACKET_AUXDATA &&
1593 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1594 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
55dab2ed
TG
1595
1596 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1597 break;
1598 }
1599 }
e5002702 1600
55dab2ed 1601 r = dhcp_packet_verify_headers(packet, len, checksum);
ac4f16ab 1602 if (r < 0)
9fadd4ca 1603 return 0;
e5002702
TG
1604
1605 len -= DHCP_IP_UDP_SIZE;
1606
022446ad 1607 return client_handle_message(client, &packet->dhcp, len);
e5002702
TG
1608}
1609
5ee482df 1610int sd_dhcp_client_start(sd_dhcp_client *client) {
6a1cd41e 1611 int r;
d3d8ac2f 1612
46a66b79 1613 assert_return(client, -EINVAL);
46a66b79 1614
0f941add
PF
1615 r = client_initialize(client);
1616 if (r < 0)
6a1cd41e 1617 return r;
8c00042c 1618
0f941add
PF
1619 if (client->last_addr)
1620 client->state = DHCP_STATE_INIT_REBOOT;
d3d8ac2f 1621
998d8047
TG
1622 r = client_start(client);
1623 if (r >= 0)
1fa2f38f 1624 log_dhcp_client(client, "STARTED on ifindex %i", client->index);
998d8047
TG
1625
1626 return r;
46a66b79
PF
1627}
1628
5ee482df 1629int sd_dhcp_client_stop(sd_dhcp_client *client) {
574cc928
TG
1630 DHCP_CLIENT_DONT_DESTROY(client);
1631
e5b04c8d
PF
1632 assert_return(client, -EINVAL);
1633
03748142 1634 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
574cc928 1635 client->state = DHCP_STATE_STOPPED;
e5b04c8d
PF
1636
1637 return 0;
bbdf06d9
PF
1638}
1639
e5002702
TG
1640int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1641 int priority) {
b25ef18b
TG
1642 int r;
1643
1644 assert_return(client, -EINVAL);
1645 assert_return(!client->event, -EBUSY);
1646
1647 if (event)
1648 client->event = sd_event_ref(event);
1649 else {
1650 r = sd_event_default(&client->event);
1651 if (r < 0)
1652 return 0;
1653 }
1654
1655 client->event_priority = priority;
1656
1657 return 0;
1658}
1659
1660int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1661 assert_return(client, -EINVAL);
1662
1663 client->event = sd_event_unref(client->event);
1664
1665 return 0;
1666}
1667
1668sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1669 if (!client)
1670 return NULL;
1671
1672 return client->event;
1673}
1674
e5b04c8d 1675sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
3733eec3
LP
1676
1677 if (!client)
1678 return NULL;
1679
1680 assert(client->n_ref >= 1);
1681 client->n_ref++;
e5b04c8d
PF
1682
1683 return client;
1684}
1685
1686sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
d2fe46b5 1687
3733eec3
LP
1688 if (!client)
1689 return NULL;
e5b04c8d 1690
3733eec3
LP
1691 assert(client->n_ref >= 1);
1692 client->n_ref--;
e5b04c8d 1693
3733eec3
LP
1694 if (client->n_ref > 0)
1695 return NULL;
e5b04c8d 1696
3733eec3 1697 log_dhcp_client(client, "FREE");
6e00a806 1698
3733eec3
LP
1699 client_initialize(client);
1700
1701 client->receive_message = sd_event_source_unref(client->receive_message);
1702
1703 sd_dhcp_client_detach_event(client);
1704
1705 sd_dhcp_lease_unref(client->lease);
1706
1707 free(client->req_opts);
1708 free(client->hostname);
1709 free(client->vendor_class_identifier);
1710 free(client);
d2fe46b5 1711
574cc928 1712 return NULL;
d2fe46b5
PF
1713}
1714
b25ef18b 1715int sd_dhcp_client_new(sd_dhcp_client **ret) {
574cc928 1716 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
011feef8 1717
b25ef18b 1718 assert_return(ret, -EINVAL);
d3d8ac2f 1719
011feef8
PF
1720 client = new0(sd_dhcp_client, 1);
1721 if (!client)
b25ef18b 1722 return -ENOMEM;
011feef8 1723
3733eec3 1724 client->n_ref = 1;
011feef8
PF
1725 client->state = DHCP_STATE_INIT;
1726 client->index = -1;
8c00042c 1727 client->fd = -1;
e2dfc79f 1728 client->attempt = 1;
324f8187 1729 client->mtu = DHCP_DEFAULT_MIN_SIZE;
011feef8
PF
1730
1731 client->req_opts_size = ELEMENTSOF(default_req_opts);
1732
1733 client->req_opts = memdup(default_req_opts, client->req_opts_size);
b25ef18b
TG
1734 if (!client->req_opts)
1735 return -ENOMEM;
1736
1737 *ret = client;
1738 client = NULL;
011feef8 1739
b25ef18b 1740 return 0;
011feef8 1741}