]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-client.c
doc: correct punctuation and improve typography in documentation
[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
b5efdb8a 31#include "alloc-util.h"
07630cea
LP
32#include "async.h"
33#include "dhcp-identifier.h"
46a66b79 34#include "dhcp-internal.h"
fe8db0c5 35#include "dhcp-lease-internal.h"
07630cea
LP
36#include "dhcp-protocol.h"
37#include "random-util.h"
38#include "string-util.h"
39#include "util.h"
011feef8 40
5bac5235 41#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
dd168445 42#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
76253e73 43
011feef8 44struct sd_dhcp_client {
3733eec3 45 unsigned n_ref;
e5b04c8d 46
011feef8 47 DHCPState state;
d3d8ac2f 48 sd_event *event;
b25ef18b 49 int event_priority;
d3d8ac2f 50 sd_event_source *timeout_resend;
011feef8 51 int index;
8c00042c
PF
52 int fd;
53 union sockaddr_union link;
54 sd_event_source *receive_message;
f5de5b00 55 bool request_broadcast;
011feef8 56 uint8_t *req_opts;
7a7c74ca 57 size_t req_opts_allocated;
011feef8 58 size_t req_opts_size;
3b349af6 59 be32_t last_addr;
76253e73
DW
60 uint8_t mac_addr[MAX_MAC_ADDR_LEN];
61 size_t mac_addr_len;
62 uint16_t arp_type;
5bac5235
TG
63 struct {
64 uint8_t type;
65 union {
66 struct {
67 /* 0: Generic (non-LL) (RFC 2132) */
68 uint8_t data[MAX_CLIENT_ID_LEN];
69 } _packed_ gen;
70 struct {
71 /* 1: Ethernet Link-Layer (RFC 2132) */
72 uint8_t haddr[ETH_ALEN];
73 } _packed_ eth;
74 struct {
75 /* 2 - 254: ARP/Link-Layer (RFC 2132) */
76 uint8_t haddr[0];
77 } _packed_ ll;
78 struct {
79 /* 255: Node-specific (RFC 4361) */
80 uint32_t iaid;
81 struct duid duid;
82 } _packed_ ns;
83 struct {
84 uint8_t data[MAX_CLIENT_ID_LEN];
85 } _packed_ raw;
86 };
87 } _packed_ client_id;
ba6c0fd6 88 size_t client_id_len;
4cc7a82c 89 char *hostname;
edb85f0d 90 char *vendor_class_identifier;
324f8187 91 uint32_t mtu;
46a66b79 92 uint32_t xid;
d3d8ac2f 93 usec_t start_time;
e2dfc79f 94 unsigned int attempt;
51debc1e
PF
95 usec_t request_sent;
96 sd_event_source *timeout_t1;
97 sd_event_source *timeout_t2;
98 sd_event_source *timeout_expire;
751246ee
PF
99 sd_dhcp_client_cb_t cb;
100 void *userdata;
a6cc569e 101 sd_dhcp_lease *lease;
011feef8
PF
102};
103
104static const uint8_t default_req_opts[] = {
105 DHCP_OPTION_SUBNET_MASK,
106 DHCP_OPTION_ROUTER,
107 DHCP_OPTION_HOST_NAME,
108 DHCP_OPTION_DOMAIN_NAME,
109 DHCP_OPTION_DOMAIN_NAME_SERVER,
011feef8
PF
110};
111
e5002702
TG
112static int client_receive_message_raw(sd_event_source *s, int fd,
113 uint32_t revents, void *userdata);
114static int client_receive_message_udp(sd_event_source *s, int fd,
115 uint32_t revents, void *userdata);
574cc928 116static void client_stop(sd_dhcp_client *client, int error);
aba26854 117
751246ee 118int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
5ee482df 119 void *userdata) {
751246ee
PF
120 assert_return(client, -EINVAL);
121
122 client->cb = cb;
123 client->userdata = userdata;
124
125 return 0;
126}
127
f5de5b00
TG
128int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
129 assert_return(client, -EINVAL);
130
131 client->request_broadcast = !!broadcast;
132
133 return 0;
134}
135
5ee482df 136int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
011feef8
PF
137 size_t i;
138
139 assert_return(client, -EINVAL);
781ca7a1
PF
140 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
141 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
142
143 switch(option) {
144 case DHCP_OPTION_PAD:
145 case DHCP_OPTION_OVERLOAD:
146 case DHCP_OPTION_MESSAGE_TYPE:
147 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
148 case DHCP_OPTION_END:
149 return -EINVAL;
150
151 default:
152 break;
153 }
154
155 for (i = 0; i < client->req_opts_size; i++)
156 if (client->req_opts[i] == option)
157 return -EEXIST;
158
7a7c74ca 159 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
011feef8
PF
160 client->req_opts_size + 1))
161 return -ENOMEM;
162
7a7c74ca 163 client->req_opts[client->req_opts_size++] = option;
011feef8
PF
164
165 return 0;
166}
167
168int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
5ee482df 169 const struct in_addr *last_addr) {
011feef8 170 assert_return(client, -EINVAL);
781ca7a1
PF
171 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
172 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
173
174 if (last_addr)
175 client->last_addr = last_addr->s_addr;
176 else
177 client->last_addr = INADDR_ANY;
178
179 return 0;
180}
181
5ee482df 182int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
011feef8 183 assert_return(client, -EINVAL);
781ca7a1
PF
184 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
185 DHCP_STATE_STOPPED), -EBUSY);
d9bf4f8c 186 assert_return(interface_index > 0, -EINVAL);
011feef8
PF
187
188 client->index = interface_index;
189
190 return 0;
191}
192
76253e73
DW
193int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr,
194 size_t addr_len, uint16_t arp_type) {
574cc928 195 DHCP_CLIENT_DONT_DESTROY(client);
4644fee0
TG
196 bool need_restart = false;
197
46a66b79 198 assert_return(client, -EINVAL);
4644fee0 199 assert_return(addr, -EINVAL);
76253e73
DW
200 assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
201 assert_return(arp_type > 0, -EINVAL);
202
203 if (arp_type == ARPHRD_ETHER)
204 assert_return(addr_len == ETH_ALEN, -EINVAL);
205 else if (arp_type == ARPHRD_INFINIBAND)
206 assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
207 else
208 return -EINVAL;
46a66b79 209
76253e73
DW
210 if (client->mac_addr_len == addr_len &&
211 memcmp(&client->mac_addr, addr, addr_len) == 0)
4644fee0
TG
212 return 0;
213
781ca7a1 214 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
4644fee0
TG
215 log_dhcp_client(client, "Changing MAC address on running DHCP "
216 "client, restarting");
4644fee0 217 need_restart = true;
03748142 218 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
4644fee0 219 }
02ec5cd7 220
76253e73
DW
221 memcpy(&client->mac_addr, addr, addr_len);
222 client->mac_addr_len = addr_len;
223 client->arp_type = arp_type;
224
ba6c0fd6
DW
225 if (need_restart && client->state != DHCP_STATE_STOPPED)
226 sd_dhcp_client_start(client);
227
228 return 0;
229}
230
231int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type,
232 const uint8_t **data, size_t *data_len) {
233
234 assert_return(client, -EINVAL);
235 assert_return(type, -EINVAL);
236 assert_return(data, -EINVAL);
237 assert_return(data_len, -EINVAL);
238
239 *type = 0;
240 *data = NULL;
241 *data_len = 0;
242 if (client->client_id_len) {
5bac5235 243 *type = client->client_id.type;
ba6c0fd6 244 *data = client->client_id.raw.data;
5bac5235 245 *data_len = client->client_id_len - sizeof(client->client_id.type);
ba6c0fd6
DW
246 }
247
248 return 0;
249}
250
251int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type,
252 const uint8_t *data, size_t data_len) {
253 DHCP_CLIENT_DONT_DESTROY(client);
254 bool need_restart = false;
255
256 assert_return(client, -EINVAL);
257 assert_return(data, -EINVAL);
258 assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
259
260 switch (type) {
261 case ARPHRD_ETHER:
262 if (data_len != ETH_ALEN)
263 return -EINVAL;
264 break;
265 case ARPHRD_INFINIBAND:
266 if (data_len != INFINIBAND_ALEN)
267 return -EINVAL;
268 break;
269 default:
270 break;
271 }
272
5bac5235
TG
273 if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
274 client->client_id.type == type &&
ba6c0fd6
DW
275 memcmp(&client->client_id.raw.data, data, data_len) == 0)
276 return 0;
277
278 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
279 log_dhcp_client(client, "Changing client ID on running DHCP "
280 "client, restarting");
281 need_restart = true;
03748142 282 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
ba6c0fd6
DW
283 }
284
5bac5235 285 client->client_id.type = type;
ba6c0fd6 286 memcpy(&client->client_id.raw.data, data, data_len);
5bac5235 287 client->client_id_len = data_len + sizeof (client->client_id.type);
46a66b79 288
781ca7a1 289 if (need_restart && client->state != DHCP_STATE_STOPPED)
4644fee0
TG
290 sd_dhcp_client_start(client);
291
46a66b79
PF
292 return 0;
293}
294
4cc7a82c
EY
295int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
296 const char *hostname) {
297 char *new_hostname = NULL;
298
299 assert_return(client, -EINVAL);
300
301 if (streq_ptr(client->hostname, hostname))
302 return 0;
303
304 if (hostname) {
305 new_hostname = strdup(hostname);
306 if (!new_hostname)
307 return -ENOMEM;
308 }
309
310 free(client->hostname);
311 client->hostname = new_hostname;
312
313 return 0;
314}
315
edb85f0d
SS
316int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client,
317 const char *vci) {
318 char *new_vci = NULL;
319
320 assert_return(client, -EINVAL);
321
322 new_vci = strdup(vci);
323 if (!new_vci)
324 return -ENOMEM;
325
326 free(client->vendor_class_identifier);
327
328 client->vendor_class_identifier = new_vci;
329
330 return 0;
331}
332
324f8187
TG
333int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
334 assert_return(client, -EINVAL);
335 assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
336
337 client->mtu = mtu;
338
339 return 0;
340}
341
a6cc569e 342int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
751246ee 343 assert_return(client, -EINVAL);
a6cc569e 344 assert_return(ret, -EINVAL);
751246ee 345
a6cc569e
TG
346 if (client->state != DHCP_STATE_BOUND &&
347 client->state != DHCP_STATE_RENEWING &&
348 client->state != DHCP_STATE_REBINDING)
349 return -EADDRNOTAVAIL;
751246ee 350
e6b18ffa 351 *ret = client->lease;
4f882b2a
TG
352
353 return 0;
354}
355
574cc928
TG
356static void client_notify(sd_dhcp_client *client, int event) {
357 if (client->cb)
751246ee 358 client->cb(client, event, client->userdata);
3e3d8f78
PF
359}
360
0f941add 361static int client_initialize(sd_dhcp_client *client) {
bbdf06d9 362 assert_return(client, -EINVAL);
bbdf06d9 363
8c00042c
PF
364 client->receive_message =
365 sd_event_source_unref(client->receive_message);
366
22fc2420 367 client->fd = asynchronous_close(client->fd);
8c00042c 368
d3d8ac2f
PF
369 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
370
51debc1e
PF
371 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
372 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
373 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
374
e2dfc79f
PF
375 client->attempt = 1;
376
f8fdefe4 377 client->state = DHCP_STATE_INIT;
0f941add 378 client->xid = 0;
bbdf06d9 379
0339cd77 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));
03748142 390 else if (error == SD_DHCP_CLIENT_EVENT_STOP)
2d2349cc
TG
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);
144c3488
TA
930 if (r < 0)
931 goto error;
6a1cd41e 932
356779df 933 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
934 if (r < 0)
935 goto error;
936
6a1cd41e
PF
937error:
938 if (r < 0)
939 client_stop(client, r);
940
941 return 0;
942
943}
944
0af03ba5
TG
945static int client_initialize_events(sd_dhcp_client *client,
946 sd_event_io_handler_t io_callback) {
947 client_initialize_io_events(client, io_callback);
948 client_initialize_time_events(client);
949
950 return 0;
951}
952
0f941add
PF
953static int client_start(sd_dhcp_client *client) {
954 int r;
955
956 assert_return(client, -EINVAL);
957 assert_return(client->event, -EINVAL);
958 assert_return(client->index > 0, -EINVAL);
959 assert_return(client->fd < 0, -EBUSY);
960 assert_return(client->xid == 0, -EINVAL);
961 assert_return(client->state == DHCP_STATE_INIT ||
962 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
963
964 client->xid = random_u32();
965
76253e73
DW
966 r = dhcp_network_bind_raw_socket(client->index, &client->link,
967 client->xid, client->mac_addr,
968 client->mac_addr_len, client->arp_type);
0f941add
PF
969 if (r < 0) {
970 client_stop(client, r);
971 return r;
972 }
0f941add 973 client->fd = r;
58ec2d3e 974
d8d74ef0 975 if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT)
fa94c34b 976 client->start_time = now(clock_boottime_or_monotonic());
0f941add 977
0f941add
PF
978 return client_initialize_events(client, client_receive_message_raw);
979}
980
51debc1e 981static int client_timeout_expire(sd_event_source *s, uint64_t usec,
5ee482df 982 void *userdata) {
751246ee 983 sd_dhcp_client *client = userdata;
574cc928 984 DHCP_CLIENT_DONT_DESTROY(client);
751246ee 985
ee57a737
TG
986 log_dhcp_client(client, "EXPIRED");
987
03748142 988 client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
0f941add 989
781ca7a1 990 /* lease was lost, start over if not freed or stopped in callback */
574cc928 991 if (client->state != DHCP_STATE_STOPPED) {
e5b04c8d
PF
992 client_initialize(client);
993 client_start(client);
994 }
751246ee 995
51debc1e
PF
996 return 0;
997}
998
5ee482df 999static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
3dd71400 1000 sd_dhcp_client *client = userdata;
574cc928 1001 DHCP_CLIENT_DONT_DESTROY(client);
3dd71400
PF
1002 int r;
1003
03e334a1 1004 client->receive_message = sd_event_source_unref(client->receive_message);
22fc2420 1005 client->fd = asynchronous_close(client->fd);
3dd71400
PF
1006
1007 client->state = DHCP_STATE_REBINDING;
1008 client->attempt = 1;
1009
76253e73
DW
1010 r = dhcp_network_bind_raw_socket(client->index, &client->link,
1011 client->xid, client->mac_addr,
1012 client->mac_addr_len, client->arp_type);
3dd71400
PF
1013 if (r < 0) {
1014 client_stop(client, r);
1015 return 0;
1016 }
3dd71400
PF
1017 client->fd = r;
1018
d23c45bf 1019 return client_initialize_events(client, client_receive_message_raw);
51debc1e
PF
1020}
1021
e5002702
TG
1022static int client_timeout_t1(sd_event_source *s, uint64_t usec,
1023 void *userdata) {
aba26854 1024 sd_dhcp_client *client = userdata;
574cc928 1025 DHCP_CLIENT_DONT_DESTROY(client);
aba26854
PF
1026
1027 client->state = DHCP_STATE_RENEWING;
1028 client->attempt = 1;
1029
0af03ba5 1030 return client_initialize_time_events(client);
51debc1e
PF
1031}
1032
e5002702
TG
1033static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
1034 size_t len) {
a6cc569e 1035 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 1036 int r;
3e3d8f78 1037
a6cc569e
TG
1038 r = dhcp_lease_new(&lease);
1039 if (r < 0)
1040 return r;
8c00042c 1041
e37f74a6
DW
1042 if (client->client_id_len) {
1043 r = dhcp_lease_set_client_id(lease,
5bac5235 1044 (uint8_t *) &client->client_id,
e37f74a6
DW
1045 client->client_id_len);
1046 if (r < 0)
1047 return r;
1048 }
1049
e5002702 1050 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
9e64dd72 1051 if (r != DHCP_OFFER) {
6ff8806e 1052 log_dhcp_client(client, "received message was not an OFFER, ignoring");
5ee482df 1053 return -ENOMSG;
9e64dd72 1054 }
8c00042c 1055
8e34a618 1056 lease->next_server = offer->siaddr;
e5002702 1057 lease->address = offer->yiaddr;
8c00042c 1058
0339cd77
LP
1059 if (lease->address == 0 ||
1060 lease->server_address == 0 ||
9e64dd72 1061 lease->lifetime == 0) {
0339cd77 1062 log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
5ee482df 1063 return -ENOMSG;
9e64dd72
TG
1064 }
1065
0339cd77 1066 if (!lease->have_subnet_mask) {
9e64dd72
TG
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 1147
03748142 1148 r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
3e3d8f78
PF
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) {
03748142 1153 r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
68ceb9df 1154 } else
03748142 1155 r = SD_DHCP_CLIENT_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
fbcd420a 1168static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) {
022446ad
TG
1169 assert(client);
1170 assert(client->request_sent);
fbcd420a 1171 assert(lifetime > 0);
022446ad 1172
fbcd420a
LP
1173 if (lifetime > 3)
1174 lifetime -= 3;
1175 else
1176 lifetime = 0;
1177
1178 return client->request_sent + (lifetime * USEC_PER_SEC * factor) +
9bf3b535 1179 + (random_u32() & 0x1fffff);
51debc1e
PF
1180}
1181
022446ad
TG
1182static int client_set_lease_timeouts(sd_dhcp_client *client) {
1183 usec_t time_now;
1184 uint64_t lifetime_timeout;
1185 uint64_t t2_timeout;
1186 uint64_t t1_timeout;
1187 char time_string[FORMAT_TIMESPAN_MAX];
5ee482df 1188 int r;
51debc1e 1189
b25ef18b
TG
1190 assert(client);
1191 assert(client->event);
022446ad
TG
1192 assert(client->lease);
1193 assert(client->lease->lifetime);
51debc1e 1194
aba26854
PF
1195 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
1196 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
1197 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
1198
022446ad
TG
1199 /* don't set timers for infinite leases */
1200 if (client->lease->lifetime == 0xffffffff)
1201 return 0;
51debc1e 1202
fa94c34b 1203 r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
022446ad
TG
1204 if (r < 0)
1205 return r;
1206 assert(client->request_sent <= time_now);
1207
1208 /* convert the various timeouts from relative (secs) to absolute (usecs) */
1209 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
fbcd420a 1210 if (client->lease->t1 > 0 && client->lease->t2 > 0) {
022446ad
TG
1211 /* both T1 and T2 are given */
1212 if (client->lease->t1 < client->lease->t2 &&
1213 client->lease->t2 < client->lease->lifetime) {
1214 /* they are both valid */
1215 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1216 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1217 } else {
1218 /* discard both */
1219 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1220 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1221 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1222 client->lease->t1 = client->lease->lifetime / 2;
1223 }
fbcd420a 1224 } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) {
022446ad
TG
1225 /* only T2 is given, and it is valid */
1226 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
1227 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1228 client->lease->t1 = client->lease->lifetime / 2;
1229 if (t2_timeout <= t1_timeout) {
1230 /* the computed T1 would be invalid, so discard T2 */
1231 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1232 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1233 }
fbcd420a 1234 } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) {
022446ad
TG
1235 /* only T1 is given, and it is valid */
1236 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
1237 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1238 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1239 if (t2_timeout <= t1_timeout) {
1240 /* the computed T2 would be invalid, so discard T1 */
1241 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1242 client->lease->t2 = client->lease->lifetime / 2;
1243 }
1244 } else {
1245 /* fall back to the default timeouts */
1246 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
1247 client->lease->t1 = client->lease->lifetime / 2;
1248 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
1249 client->lease->t2 = (client->lease->lifetime * 7) / 8;
1250 }
51debc1e 1251
022446ad 1252 /* arm lifetime timeout */
6a0f1f6d 1253 r = sd_event_add_time(client->event, &client->timeout_expire,
fa94c34b 1254 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1255 lifetime_timeout, 10 * USEC_PER_MSEC,
1256 client_timeout_expire, client);
5ee482df
TG
1257 if (r < 0)
1258 return r;
51debc1e 1259
022446ad 1260 r = sd_event_source_set_priority(client->timeout_expire,
e5002702 1261 client->event_priority);
b25ef18b
TG
1262 if (r < 0)
1263 return r;
1264
356779df 1265 r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime");
9021bb9f
TG
1266 if (r < 0)
1267 return r;
1268
022446ad 1269 log_dhcp_client(client, "lease expires in %s",
ed19c567 1270 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_timeout - time_now, USEC_PER_SEC));
51debc1e 1271
022446ad
TG
1272 /* don't arm earlier timeouts if this has already expired */
1273 if (lifetime_timeout <= time_now)
1274 return 0;
51debc1e 1275
022446ad 1276 /* arm T2 timeout */
6a0f1f6d
LP
1277 r = sd_event_add_time(client->event,
1278 &client->timeout_t2,
fa94c34b 1279 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1280 t2_timeout,
1281 10 * USEC_PER_MSEC,
1282 client_timeout_t2, client);
5ee482df
TG
1283 if (r < 0)
1284 return r;
51debc1e 1285
e5002702
TG
1286 r = sd_event_source_set_priority(client->timeout_t2,
1287 client->event_priority);
b25ef18b
TG
1288 if (r < 0)
1289 return r;
1290
356779df 1291 r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout");
9021bb9f
TG
1292 if (r < 0)
1293 return r;
1294
022446ad 1295 log_dhcp_client(client, "T2 expires in %s",
ed19c567 1296 format_timespan(time_string, FORMAT_TIMESPAN_MAX, t2_timeout - time_now, USEC_PER_SEC));
022446ad
TG
1297
1298 /* don't arm earlier timeout if this has already expired */
1299 if (t2_timeout <= time_now)
1300 return 0;
51debc1e 1301
022446ad 1302 /* arm T1 timeout */
6a0f1f6d
LP
1303 r = sd_event_add_time(client->event,
1304 &client->timeout_t1,
fa94c34b 1305 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1306 t1_timeout, 10 * USEC_PER_MSEC,
1307 client_timeout_t1, client);
5ee482df
TG
1308 if (r < 0)
1309 return r;
51debc1e 1310
022446ad 1311 r = sd_event_source_set_priority(client->timeout_t1,
e5002702 1312 client->event_priority);
b25ef18b
TG
1313 if (r < 0)
1314 return r;
1315
356779df 1316 r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer");
9021bb9f
TG
1317 if (r < 0)
1318 return r;
1319
022446ad 1320 log_dhcp_client(client, "T1 expires in %s",
ed19c567 1321 format_timespan(time_string, FORMAT_TIMESPAN_MAX, t1_timeout - time_now, USEC_PER_SEC));
022446ad 1322
51debc1e
PF
1323 return 0;
1324}
1325
e5002702 1326static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
022446ad 1327 int len) {
574cc928 1328 DHCP_CLIENT_DONT_DESTROY(client);
e5002702 1329 int r = 0, notify_event = 0;
8c00042c 1330
b25ef18b
TG
1331 assert(client);
1332 assert(client->event);
e5002702 1333 assert(message);
b25ef18b 1334
8c00042c
PF
1335 switch (client->state) {
1336 case DHCP_STATE_SELECTING:
1337
e5002702
TG
1338 r = client_handle_offer(client, message, len);
1339 if (r >= 0) {
8c00042c 1340
8c00042c
PF
1341 client->timeout_resend =
1342 sd_event_source_unref(client->timeout_resend);
1343
1344 client->state = DHCP_STATE_REQUESTING;
e2dfc79f
PF
1345 client->attempt = 1;
1346
6a0f1f6d
LP
1347 r = sd_event_add_time(client->event,
1348 &client->timeout_resend,
fa94c34b 1349 clock_boottime_or_monotonic(),
6a0f1f6d
LP
1350 0, 0,
1351 client_timeout_resend, client);
3e3d8f78 1352 if (r < 0)
e2dfc79f 1353 goto error;
b25ef18b 1354
e5002702
TG
1355 r = sd_event_source_set_priority(client->timeout_resend,
1356 client->event_priority);
b25ef18b
TG
1357 if (r < 0)
1358 goto error;
9021bb9f 1359
356779df 1360 r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer");
9021bb9f
TG
1361 if (r < 0)
1362 goto error;
9e64dd72
TG
1363 } else if (r == -ENOMSG)
1364 /* invalid message, let's ignore it */
1365 return 0;
8c00042c
PF
1366
1367 break;
1368
8b1243f7 1369 case DHCP_STATE_REBOOTING:
3e3d8f78 1370 case DHCP_STATE_REQUESTING:
aba26854 1371 case DHCP_STATE_RENEWING:
3dd71400 1372 case DHCP_STATE_REBINDING:
aba26854 1373
e5002702 1374 r = client_handle_ack(client, message, len);
2d2349cc 1375 if (r >= 0) {
3e3d8f78
PF
1376 client->timeout_resend =
1377 sd_event_source_unref(client->timeout_resend);
affaa94f
DW
1378 client->receive_message =
1379 sd_event_source_unref(client->receive_message);
1380 client->fd = asynchronous_close(client->fd);
3e3d8f78 1381
8b1243f7
PF
1382 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1383 DHCP_STATE_REBOOTING))
03748142
DH
1384 notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
1385 else if (r != SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
aba26854
PF
1386 notify_event = r;
1387
3e3d8f78
PF
1388 client->state = DHCP_STATE_BOUND;
1389 client->attempt = 1;
1390
1391 client->last_addr = client->lease->address;
1392
022446ad 1393 r = client_set_lease_timeouts(client);
0411760a
TG
1394 if (r < 0) {
1395 log_dhcp_client(client, "could not set lease timeouts");
51debc1e 1396 goto error;
0411760a 1397 }
51debc1e 1398
0af03ba5
TG
1399 r = dhcp_network_bind_udp_socket(client->lease->address,
1400 DHCP_PORT_CLIENT);
1401 if (r < 0) {
1402 log_dhcp_client(client, "could not bind UDP socket");
1403 goto error;
1404 }
1405
1406 client->fd = r;
1407
1408 client_initialize_io_events(client, client_receive_message_udp);
1409
e5b04c8d 1410 if (notify_event) {
574cc928
TG
1411 client_notify(client, notify_event);
1412 if (client->state == DHCP_STATE_STOPPED)
e5b04c8d
PF
1413 return 0;
1414 }
3e3d8f78 1415
2d2349cc
TG
1416 } else if (r == -EADDRNOTAVAIL) {
1417 /* got a NAK, let's restart the client */
1418 client->timeout_resend =
1419 sd_event_source_unref(client->timeout_resend);
1420
1421 r = client_initialize(client);
1422 if (r < 0)
1423 goto error;
1424
1425 r = client_start(client);
1426 if (r < 0)
1427 goto error;
1428
1429 log_dhcp_client(client, "REBOOTED");
1430
1431 return 0;
9e64dd72
TG
1432 } else if (r == -ENOMSG)
1433 /* invalid message, let's ignore it */
1434 return 0;
97b9372d 1435
3e3d8f78
PF
1436 break;
1437
615c1467
TG
1438 case DHCP_STATE_BOUND:
1439 r = client_handle_forcerenew(client, message, len);
1440 if (r >= 0) {
1441 r = client_timeout_t1(NULL, 0, client);
1442 if (r < 0)
1443 goto error;
1444 } else if (r == -ENOMSG)
1445 /* invalid message, let's ignore it */
1446 return 0;
1447
1448 break;
1449
8c00042c
PF
1450 case DHCP_STATE_INIT:
1451 case DHCP_STATE_INIT_REBOOT:
8c00042c
PF
1452
1453 break;
781ca7a1
PF
1454
1455 case DHCP_STATE_STOPPED:
1456 r = -EINVAL;
1457 goto error;
8c00042c
PF
1458 }
1459
1460error:
2d2349cc 1461 if (r < 0)
e5b04c8d 1462 client_stop(client, r);
e2dfc79f 1463
e5b04c8d 1464 return r;
8c00042c
PF
1465}
1466
8f61afd8 1467static int client_receive_message_udp(sd_event_source *s, int fd,
e5002702
TG
1468 uint32_t revents, void *userdata) {
1469 sd_dhcp_client *client = userdata;
5266a81e
TG
1470 _cleanup_free_ DHCPMessage *message = NULL;
1471 int buflen = 0, len, r;
76253e73 1472 const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } };
3dc05554 1473 const struct ether_addr *expected_chaddr = NULL;
76253e73 1474 uint8_t expected_hlen = 0;
e5002702
TG
1475
1476 assert(s);
1477 assert(client);
e5002702 1478
5266a81e 1479 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1480 if (r < 0)
1481 return r;
1482
1483 if (buflen < 0)
1484 /* this can't be right */
1485 return -EIO;
5266a81e
TG
1486
1487 message = malloc0(buflen);
1488 if (!message)
1489 return -ENOMEM;
1490
1491 len = read(fd, message, buflen);
0c79c68d
TG
1492 if (len < 0) {
1493 log_dhcp_client(client, "could not receive message from UDP "
590b6b91 1494 "socket: %m");
0c79c68d 1495 return 0;
9fbc2523
TG
1496 } else if ((size_t)len < sizeof(DHCPMessage)) {
1497 log_dhcp_client(client, "too small to be a DHCP message: ignoring");
e5002702 1498 return 0;
9fbc2523
TG
1499 }
1500
1501 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1502 log_dhcp_client(client, "not a DHCP message: ignoring");
1503 return 0;
1504 }
1505
1506 if (message->op != BOOTREPLY) {
1507 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1508 return 0;
1509 }
1510
76253e73
DW
1511 if (message->htype != client->arp_type) {
1512 log_dhcp_client(client, "packet type does not match client type");
1513 return 0;
1514 }
1515
1516 if (client->arp_type == ARPHRD_ETHER) {
1517 expected_hlen = ETH_ALEN;
3dc05554 1518 expected_chaddr = (const struct ether_addr *) &client->mac_addr;
76253e73
DW
1519 } else {
1520 /* Non-ethernet links expect zero chaddr */
1521 expected_hlen = 0;
3dc05554 1522 expected_chaddr = &zero_mac;
76253e73
DW
1523 }
1524
1525 if (message->hlen != expected_hlen) {
1526 log_dhcp_client(client, "unexpected packet hlen %d", message->hlen);
9fbc2523
TG
1527 return 0;
1528 }
1529
3dc05554 1530 if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) {
9fbc2523
TG
1531 log_dhcp_client(client, "received chaddr does not match "
1532 "expected: ignoring");
1533 return 0;
1534 }
e5002702 1535
615c1467
TG
1536 if (client->state != DHCP_STATE_BOUND &&
1537 be32toh(message->xid) != client->xid) {
1538 /* in BOUND state, we may receive FORCERENEW with xid set by server,
1539 so ignore the xid in this case */
1540 log_dhcp_client(client, "received xid (%u) does not match "
1541 "expected (%u): ignoring",
1542 be32toh(message->xid), client->xid);
1543 return 0;
1544 }
1545
022446ad 1546 return client_handle_message(client, message, len);
e5002702
TG
1547}
1548
8f61afd8 1549static int client_receive_message_raw(sd_event_source *s, int fd,
e5002702
TG
1550 uint32_t revents, void *userdata) {
1551 sd_dhcp_client *client = userdata;
5266a81e 1552 _cleanup_free_ DHCPPacket *packet = NULL;
55dab2ed
TG
1553 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1554 struct iovec iov = {};
1555 struct msghdr msg = {
1556 .msg_iov = &iov,
1557 .msg_iovlen = 1,
1558 .msg_control = cmsgbuf,
1559 .msg_controllen = sizeof(cmsgbuf),
1560 };
1561 struct cmsghdr *cmsg;
1562 bool checksum = true;
1563 int buflen = 0, len, r;
e5002702
TG
1564
1565 assert(s);
1566 assert(client);
e5002702 1567
5266a81e 1568 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1569 if (r < 0)
1570 return r;
1571
1572 if (buflen < 0)
1573 /* this can't be right */
1574 return -EIO;
5266a81e
TG
1575
1576 packet = malloc0(buflen);
1577 if (!packet)
1578 return -ENOMEM;
1579
55dab2ed
TG
1580 iov.iov_base = packet;
1581 iov.iov_len = buflen;
1582
a38d9945 1583 len = recvmsg(fd, &msg, 0);
55dab2ed
TG
1584 if (len < 0) {
1585 log_dhcp_client(client, "could not receive message from raw "
590b6b91 1586 "socket: %m");
e5002702 1587 return 0;
0c79c68d
TG
1588 } else if ((size_t)len < sizeof(DHCPPacket))
1589 return 0;
55dab2ed 1590
2a1288ff 1591 CMSG_FOREACH(cmsg, &msg) {
48a4612e
TG
1592 if (cmsg->cmsg_level == SOL_PACKET &&
1593 cmsg->cmsg_type == PACKET_AUXDATA &&
1594 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1595 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
55dab2ed
TG
1596
1597 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1598 break;
1599 }
1600 }
e5002702 1601
55dab2ed 1602 r = dhcp_packet_verify_headers(packet, len, checksum);
ac4f16ab 1603 if (r < 0)
9fadd4ca 1604 return 0;
e5002702
TG
1605
1606 len -= DHCP_IP_UDP_SIZE;
1607
022446ad 1608 return client_handle_message(client, &packet->dhcp, len);
e5002702
TG
1609}
1610
5ee482df 1611int sd_dhcp_client_start(sd_dhcp_client *client) {
6a1cd41e 1612 int r;
d3d8ac2f 1613
46a66b79 1614 assert_return(client, -EINVAL);
46a66b79 1615
0f941add
PF
1616 r = client_initialize(client);
1617 if (r < 0)
6a1cd41e 1618 return r;
8c00042c 1619
0f941add
PF
1620 if (client->last_addr)
1621 client->state = DHCP_STATE_INIT_REBOOT;
d3d8ac2f 1622
998d8047
TG
1623 r = client_start(client);
1624 if (r >= 0)
1fa2f38f 1625 log_dhcp_client(client, "STARTED on ifindex %i", client->index);
998d8047
TG
1626
1627 return r;
46a66b79
PF
1628}
1629
5ee482df 1630int sd_dhcp_client_stop(sd_dhcp_client *client) {
574cc928
TG
1631 DHCP_CLIENT_DONT_DESTROY(client);
1632
e5b04c8d
PF
1633 assert_return(client, -EINVAL);
1634
03748142 1635 client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
574cc928 1636 client->state = DHCP_STATE_STOPPED;
e5b04c8d
PF
1637
1638 return 0;
bbdf06d9
PF
1639}
1640
e5002702
TG
1641int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1642 int priority) {
b25ef18b
TG
1643 int r;
1644
1645 assert_return(client, -EINVAL);
1646 assert_return(!client->event, -EBUSY);
1647
1648 if (event)
1649 client->event = sd_event_ref(event);
1650 else {
1651 r = sd_event_default(&client->event);
1652 if (r < 0)
1653 return 0;
1654 }
1655
1656 client->event_priority = priority;
1657
1658 return 0;
1659}
1660
1661int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1662 assert_return(client, -EINVAL);
1663
1664 client->event = sd_event_unref(client->event);
1665
1666 return 0;
1667}
1668
1669sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1670 if (!client)
1671 return NULL;
1672
1673 return client->event;
1674}
1675
e5b04c8d 1676sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
3733eec3
LP
1677
1678 if (!client)
1679 return NULL;
1680
1681 assert(client->n_ref >= 1);
1682 client->n_ref++;
e5b04c8d
PF
1683
1684 return client;
1685}
1686
1687sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
d2fe46b5 1688
3733eec3
LP
1689 if (!client)
1690 return NULL;
e5b04c8d 1691
3733eec3
LP
1692 assert(client->n_ref >= 1);
1693 client->n_ref--;
e5b04c8d 1694
3733eec3
LP
1695 if (client->n_ref > 0)
1696 return NULL;
e5b04c8d 1697
3733eec3 1698 log_dhcp_client(client, "FREE");
6e00a806 1699
3733eec3
LP
1700 client_initialize(client);
1701
1702 client->receive_message = sd_event_source_unref(client->receive_message);
1703
1704 sd_dhcp_client_detach_event(client);
1705
1706 sd_dhcp_lease_unref(client->lease);
1707
1708 free(client->req_opts);
1709 free(client->hostname);
1710 free(client->vendor_class_identifier);
1711 free(client);
d2fe46b5 1712
574cc928 1713 return NULL;
d2fe46b5
PF
1714}
1715
b25ef18b 1716int sd_dhcp_client_new(sd_dhcp_client **ret) {
574cc928 1717 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
011feef8 1718
b25ef18b 1719 assert_return(ret, -EINVAL);
d3d8ac2f 1720
011feef8
PF
1721 client = new0(sd_dhcp_client, 1);
1722 if (!client)
b25ef18b 1723 return -ENOMEM;
011feef8 1724
3733eec3 1725 client->n_ref = 1;
011feef8
PF
1726 client->state = DHCP_STATE_INIT;
1727 client->index = -1;
8c00042c 1728 client->fd = -1;
e2dfc79f 1729 client->attempt = 1;
324f8187 1730 client->mtu = DHCP_DEFAULT_MIN_SIZE;
011feef8
PF
1731
1732 client->req_opts_size = ELEMENTSOF(default_req_opts);
1733
1734 client->req_opts = memdup(default_req_opts, client->req_opts_size);
b25ef18b
TG
1735 if (!client->req_opts)
1736 return -ENOMEM;
1737
1738 *ret = client;
1739 client = NULL;
011feef8 1740
b25ef18b 1741 return 0;
011feef8 1742}