]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/sd-dhcp-client.c
sd-dhcp-client/networkd: set lifetimes for IPv4 addresses
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-client.c
CommitLineData
011feef8
PF
1/***
2 This file is part of systemd.
3
4 Copyright (C) 2013 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <stdlib.h>
21#include <errno.h>
22#include <string.h>
23#include <stdio.h>
46a66b79 24#include <net/ethernet.h>
3b7ca119 25#include <net/if_arp.h>
63175195 26#include <netinet/ether.h>
aba26854 27#include <sys/param.h>
5266a81e 28#include <sys/ioctl.h>
011feef8
PF
29
30#include "util.h"
31#include "list.h"
e5b04c8d 32#include "refcnt.h"
22fc2420 33#include "async.h"
011feef8
PF
34
35#include "dhcp-protocol.h"
46a66b79 36#include "dhcp-internal.h"
fe8db0c5 37#include "dhcp-lease-internal.h"
011feef8
PF
38#include "sd-dhcp-client.h"
39
40struct sd_dhcp_client {
e5b04c8d
PF
41 RefCount n_ref;
42
011feef8 43 DHCPState state;
d3d8ac2f 44 sd_event *event;
b25ef18b 45 int event_priority;
d3d8ac2f 46 sd_event_source *timeout_resend;
011feef8 47 int index;
8c00042c
PF
48 int fd;
49 union sockaddr_union link;
50 sd_event_source *receive_message;
011feef8 51 uint8_t *req_opts;
7a7c74ca 52 size_t req_opts_allocated;
011feef8 53 size_t req_opts_size;
3b349af6 54 be32_t last_addr;
715c6a9a
PF
55 struct {
56 uint8_t type;
57 struct ether_addr mac_addr;
58 } _packed_ client_id;
46a66b79 59 uint32_t xid;
d3d8ac2f 60 usec_t start_time;
40e39f62 61 uint16_t secs;
e2dfc79f 62 unsigned int attempt;
51debc1e
PF
63 usec_t request_sent;
64 sd_event_source *timeout_t1;
65 sd_event_source *timeout_t2;
66 sd_event_source *timeout_expire;
751246ee
PF
67 sd_dhcp_client_cb_t cb;
68 void *userdata;
a6cc569e 69 sd_dhcp_lease *lease;
011feef8
PF
70};
71
72static const uint8_t default_req_opts[] = {
73 DHCP_OPTION_SUBNET_MASK,
74 DHCP_OPTION_ROUTER,
75 DHCP_OPTION_HOST_NAME,
76 DHCP_OPTION_DOMAIN_NAME,
77 DHCP_OPTION_DOMAIN_NAME_SERVER,
78 DHCP_OPTION_NTP_SERVER,
79};
80
e5002702
TG
81static int client_receive_message_raw(sd_event_source *s, int fd,
82 uint32_t revents, void *userdata);
83static int client_receive_message_udp(sd_event_source *s, int fd,
84 uint32_t revents, void *userdata);
574cc928 85static void client_stop(sd_dhcp_client *client, int error);
aba26854 86
751246ee 87int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
5ee482df 88 void *userdata) {
751246ee
PF
89 assert_return(client, -EINVAL);
90
91 client->cb = cb;
92 client->userdata = userdata;
93
94 return 0;
95}
96
5ee482df 97int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
011feef8
PF
98 size_t i;
99
100 assert_return(client, -EINVAL);
781ca7a1
PF
101 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
102 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
103
104 switch(option) {
105 case DHCP_OPTION_PAD:
106 case DHCP_OPTION_OVERLOAD:
107 case DHCP_OPTION_MESSAGE_TYPE:
108 case DHCP_OPTION_PARAMETER_REQUEST_LIST:
109 case DHCP_OPTION_END:
110 return -EINVAL;
111
112 default:
113 break;
114 }
115
116 for (i = 0; i < client->req_opts_size; i++)
117 if (client->req_opts[i] == option)
118 return -EEXIST;
119
7a7c74ca 120 if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
011feef8
PF
121 client->req_opts_size + 1))
122 return -ENOMEM;
123
7a7c74ca 124 client->req_opts[client->req_opts_size++] = option;
011feef8
PF
125
126 return 0;
127}
128
129int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
5ee482df 130 const struct in_addr *last_addr) {
011feef8 131 assert_return(client, -EINVAL);
781ca7a1
PF
132 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
133 DHCP_STATE_STOPPED), -EBUSY);
011feef8
PF
134
135 if (last_addr)
136 client->last_addr = last_addr->s_addr;
137 else
138 client->last_addr = INADDR_ANY;
139
140 return 0;
141}
142
5ee482df 143int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
011feef8 144 assert_return(client, -EINVAL);
781ca7a1
PF
145 assert_return (IN_SET(client->state, DHCP_STATE_INIT,
146 DHCP_STATE_STOPPED), -EBUSY);
d9bf4f8c 147 assert_return(interface_index > 0, -EINVAL);
011feef8
PF
148
149 client->index = interface_index;
150
151 return 0;
152}
153
46a66b79 154int sd_dhcp_client_set_mac(sd_dhcp_client *client,
5ee482df 155 const struct ether_addr *addr) {
574cc928 156 DHCP_CLIENT_DONT_DESTROY(client);
4644fee0
TG
157 bool need_restart = false;
158
46a66b79 159 assert_return(client, -EINVAL);
4644fee0 160 assert_return(addr, -EINVAL);
46a66b79 161
4644fee0
TG
162 if (memcmp(&client->client_id.mac_addr, addr, ETH_ALEN) == 0)
163 return 0;
164
781ca7a1 165 if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
4644fee0
TG
166 log_dhcp_client(client, "Changing MAC address on running DHCP "
167 "client, restarting");
4644fee0 168 need_restart = true;
574cc928 169 client_stop(client, DHCP_EVENT_STOP);
4644fee0 170 }
02ec5cd7 171
715c6a9a
PF
172 memcpy(&client->client_id.mac_addr, addr, ETH_ALEN);
173 client->client_id.type = 0x01;
46a66b79 174
781ca7a1 175 if (need_restart && client->state != DHCP_STATE_STOPPED)
4644fee0
TG
176 sd_dhcp_client_start(client);
177
46a66b79
PF
178 return 0;
179}
180
a6cc569e 181int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
751246ee 182 assert_return(client, -EINVAL);
a6cc569e 183 assert_return(ret, -EINVAL);
751246ee 184
a6cc569e
TG
185 if (client->state != DHCP_STATE_BOUND &&
186 client->state != DHCP_STATE_RENEWING &&
187 client->state != DHCP_STATE_REBINDING)
188 return -EADDRNOTAVAIL;
751246ee 189
a6cc569e 190 *ret = sd_dhcp_lease_ref(client->lease);
4f882b2a
TG
191
192 return 0;
193}
194
574cc928
TG
195static void client_notify(sd_dhcp_client *client, int event) {
196 if (client->cb)
751246ee 197 client->cb(client, event, client->userdata);
3e3d8f78
PF
198}
199
0f941add 200static int client_initialize(sd_dhcp_client *client) {
bbdf06d9 201 assert_return(client, -EINVAL);
bbdf06d9 202
8c00042c
PF
203 client->receive_message =
204 sd_event_source_unref(client->receive_message);
205
22fc2420 206 client->fd = asynchronous_close(client->fd);
8c00042c 207
d3d8ac2f
PF
208 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
209
51debc1e
PF
210 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
211 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
212 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
213
e2dfc79f
PF
214 client->attempt = 1;
215
f8fdefe4 216 client->state = DHCP_STATE_INIT;
0f941add 217 client->xid = 0;
bbdf06d9 218
a6cc569e
TG
219 if (client->lease)
220 client->lease = sd_dhcp_lease_unref(client->lease);
8c00042c 221
0f941add
PF
222 return 0;
223}
224
574cc928
TG
225static void client_stop(sd_dhcp_client *client, int error) {
226 assert(client);
0f941add 227
ccfdc9a1
UTL
228 if (error < 0)
229 log_dhcp_client(client, "STOPPED: %s", strerror(-error));
230 else {
231 switch(error) {
232 case DHCP_EVENT_STOP:
699370c9 233 log_dhcp_client(client, "STOPPED");
ccfdc9a1
UTL
234 break;
235 case DHCP_EVENT_NO_LEASE:
236 log_dhcp_client(client, "STOPPED: No lease");
237 break;
238 default:
239 log_dhcp_client(client, "STOPPED: Unknown reason");
240 break;
241 }
242 }
0f941add 243
574cc928 244 client_notify(client, error);
ee57a737 245
574cc928 246 client_initialize(client);
bbdf06d9
PF
247}
248
424a8732
TG
249static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
250 uint8_t type, size_t *_optlen, size_t *_optoffset) {
251 _cleanup_free_ DHCPPacket *packet;
252 size_t optlen, optoffset, size;
8a9e7616 253 be16_t max_size;
cf597f65 254 int r;
46a66b79 255
6236f49b
TG
256 assert(client);
257 assert(client->secs);
424a8732
TG
258 assert(ret);
259 assert(_optlen);
260 assert(_optoffset);
8a9e7616 261 assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);
0a1b6da8 262
424a8732
TG
263 optlen = DHCP_MIN_OPTIONS_SIZE;
264 size = sizeof(DHCPPacket) + optlen;
265
266 packet = malloc0(size);
267 if (!packet)
268 return -ENOMEM;
269
270 r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
271 optlen, &optoffset);
cf597f65
TG
272 if (r < 0)
273 return r;
46a66b79 274
0a1b6da8
TG
275 /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
276 refuse to issue an DHCP lease if 'secs' is set to zero */
424a8732 277 packet->dhcp.secs = htobe16(client->secs);
0a1b6da8 278
63a07041
CA
279 /* RFC2132 section 4.1
280 A client that cannot receive unicast IP datagrams until its protocol
281 software has been configured with an IP address SHOULD set the
282 BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
283 DHCPREQUEST messages that client sends. The BROADCAST bit will
284 provide a hint to the DHCP server and BOOTP relay agent to broadcast
285 any messages to the client on the client's subnet. */
286 packet->dhcp.flags = htobe16(0x8000);
287
50d6810e
TG
288 /* RFC2132 section 4.1.1:
289 The client MUST include its hardware address in the ’chaddr’ field, if
290 necessary for delivery of DHCP reply messages.
291 */
424a8732 292 memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);
f5a70de7 293
ee57a737 294 /* Some DHCP servers will refuse to issue an DHCP lease if the Client
46a66b79 295 Identifier option is not set */
424a8732 296 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
20b958bf 297 DHCP_OPTION_CLIENT_IDENTIFIER,
715c6a9a 298 sizeof(client->client_id), &client->client_id);
cf597f65
TG
299 if (r < 0)
300 return r;
46a66b79 301
50d6810e
TG
302
303 /* RFC2131 section 3.5:
304 in its initial DHCPDISCOVER or DHCPREQUEST message, a
305 client may provide the server with a list of specific
306 parameters the client is interested in. If the client
307 includes a list of parameters in a DHCPDISCOVER message,
308 it MUST include that list in any subsequent DHCPREQUEST
309 messages.
310 */
424a8732 311 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
8a9e7616 312 DHCP_OPTION_PARAMETER_REQUEST_LIST,
20b958bf 313 client->req_opts_size, client->req_opts);
8a9e7616
TG
314 if (r < 0)
315 return r;
316
50d6810e
TG
317 /* RFC2131 section 3.5:
318 The client SHOULD include the ’maximum DHCP message size’ option to
319 let the server know how large the server may make its DHCP messages.
320
321 Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
322 than the defined default size unless the Maximum Messge Size option
323 is explicitely set
324 */
424a8732
TG
325 max_size = htobe16(size);
326 r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
8a9e7616
TG
327 DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
328 2, &max_size);
329 if (r < 0)
330 return r;
46a66b79 331
424a8732
TG
332 *_optlen = optlen;
333 *_optoffset = optoffset;
334 *ret = packet;
335 packet = NULL;
336
46a66b79
PF
337 return 0;
338}
339
63edaa62
TG
340static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
341 size_t len) {
342 dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
343 INADDR_BROADCAST, DHCP_PORT_SERVER, len);
344
345 return dhcp_network_send_raw_socket(client->fd, &client->link,
346 packet, len);
347}
348
6236f49b 349static int client_send_discover(sd_dhcp_client *client) {
9f2a50a3 350 _cleanup_free_ DHCPPacket *discover = NULL;
424a8732 351 size_t optoffset, optlen;
6236f49b
TG
352 usec_t time_now;
353 int r;
354
355 assert(client);
50d6810e
TG
356 assert(client->state == DHCP_STATE_INIT ||
357 client->state == DHCP_STATE_SELECTING);
358
359 /* See RFC2131 section 4.4.1 */
6236f49b 360
6a0f1f6d 361 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
6236f49b
TG
362 if (r < 0)
363 return r;
364 assert(time_now >= client->start_time);
365
366 /* seconds between sending first and last DISCOVER
367 * must always be strictly positive to deal with broken servers */
368 client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
46a66b79 369
424a8732
TG
370 r = client_message_init(client, &discover, DHCP_DISCOVER,
371 &optlen, &optoffset);
6236f49b
TG
372 if (r < 0)
373 return r;
46a66b79 374
50d6810e
TG
375 /* the client may suggest values for the network address
376 and lease time in the DHCPDISCOVER message. The client may include
377 the ’requested IP address’ option to suggest that a particular IP
378 address be assigned, and may include the ’IP address lease time’
379 option to suggest the lease time it would like.
380 */
46a66b79 381 if (client->last_addr != INADDR_ANY) {
04b28be1 382 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf
TG
383 DHCP_OPTION_REQUESTED_IP_ADDRESS,
384 4, &client->last_addr);
6236f49b
TG
385 if (r < 0)
386 return r;
46a66b79
PF
387 }
388
04b28be1 389 r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
20b958bf 390 DHCP_OPTION_END, 0, NULL);
f7926298
TA
391 if (r < 0)
392 return r;
46a66b79 393
50d6810e
TG
394 /* We currently ignore:
395 The client SHOULD wait a random time between one and ten seconds to
396 desynchronize the use of DHCP at startup.
397 */
20b958bf 398 r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
6236f49b
TG
399 if (r < 0)
400 return r;
e2dfc79f 401
ee57a737
TG
402 log_dhcp_client(client, "DISCOVER");
403
63edaa62 404 return 0;
e2dfc79f
PF
405}
406
6236f49b 407static int client_send_request(sd_dhcp_client *client) {
8186d9dd 408 _cleanup_free_ DHCPPacket *request = NULL;
424a8732 409 size_t optoffset, optlen;
6236f49b 410 int r;
e2dfc79f 411
424a8732
TG
412 r = client_message_init(client, &request, DHCP_REQUEST,
413 &optlen, &optoffset);
6236f49b
TG
414 if (r < 0)
415 return r;
46a66b79 416
8b1243f7 417 switch (client->state) {
50d6810e
TG
418 /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
419 SELECTING should be REQUESTING)
420 */
421
422 case DHCP_STATE_REQUESTING:
423 /* Client inserts the address of the selected server in ’server
424 identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
425 filled in with the yiaddr value from the chosen DHCPOFFER.
426 */
8b1243f7 427
04b28be1 428 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
429 DHCP_OPTION_SERVER_IDENTIFIER,
430 4, &client->lease->server_address);
6236f49b
TG
431 if (r < 0)
432 return r;
8b1243f7 433
04b28be1 434 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
6236f49b
TG
435 DHCP_OPTION_REQUESTED_IP_ADDRESS,
436 4, &client->lease->address);
437 if (r < 0)
438 return r;
439
50d6810e
TG
440 break;
441
442 case DHCP_STATE_INIT_REBOOT:
443 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
444 option MUST be filled in with client’s notion of its previously
445 assigned address. ’ciaddr’ MUST be zero.
446 */
04b28be1 447 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
50d6810e
TG
448 DHCP_OPTION_REQUESTED_IP_ADDRESS,
449 4, &client->last_addr);
6236f49b
TG
450 if (r < 0)
451 return r;
8b1243f7
PF
452 break;
453
8b1243f7 454 case DHCP_STATE_RENEWING:
50d6810e
TG
455 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
456 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
457 client’s IP address.
458 */
459
460 /* fall through */
8b1243f7 461 case DHCP_STATE_REBINDING:
50d6810e
TG
462 /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
463 option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
464 client’s IP address.
465
466 This message MUST be broadcast to the 0xffffffff IP broadcast address.
467 */
468 request->dhcp.ciaddr = client->lease->address;
8b1243f7
PF
469
470 break;
781ca7a1 471
50d6810e
TG
472 case DHCP_STATE_INIT:
473 case DHCP_STATE_SELECTING:
474 case DHCP_STATE_REBOOTING:
475 case DHCP_STATE_BOUND:
781ca7a1
PF
476 case DHCP_STATE_STOPPED:
477 return -EINVAL;
e2dfc79f 478 }
46a66b79 479
04b28be1 480 r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
20b958bf 481 DHCP_OPTION_END, 0, NULL);
6236f49b
TG
482 if (r < 0)
483 return r;
46a66b79 484
aba26854 485 if (client->state == DHCP_STATE_RENEWING) {
6236f49b
TG
486 r = dhcp_network_send_udp_socket(client->fd,
487 client->lease->server_address,
488 DHCP_PORT_SERVER,
489 &request->dhcp,
20b958bf 490 sizeof(DHCPMessage) + optoffset);
aba26854 491 } else {
20b958bf 492 r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
aba26854 493 }
6236f49b
TG
494 if (r < 0)
495 return r;
46a66b79 496
998d8047
TG
497 switch (client->state) {
498 case DHCP_STATE_REQUESTING:
499 log_dhcp_client(client, "REQUEST (requesting)");
500 break;
501 case DHCP_STATE_INIT_REBOOT:
502 log_dhcp_client(client, "REQUEST (init-reboot)");
503 break;
504 case DHCP_STATE_RENEWING:
505 log_dhcp_client(client, "REQUEST (renewing)");
506 break;
507 case DHCP_STATE_REBINDING:
508 log_dhcp_client(client, "REQUEST (rebinding)");
509 break;
510 default:
511 log_dhcp_client(client, "REQUEST (invalid)");
512 break;
513 }
ee57a737 514
63edaa62 515 return 0;
46a66b79
PF
516}
517
7739a40b
TG
518static int client_start(sd_dhcp_client *client);
519
d3d8ac2f 520static int client_timeout_resend(sd_event_source *s, uint64_t usec,
5ee482df 521 void *userdata) {
d3d8ac2f 522 sd_dhcp_client *client = userdata;
574cc928 523 DHCP_CLIENT_DONT_DESTROY(client);
aba26854 524 usec_t next_timeout = 0;
d23c45bf 525 uint64_t time_now;
aba26854 526 uint32_t time_left;
d23c45bf 527 int r;
b25ef18b
TG
528
529 assert(s);
530 assert(client);
531 assert(client->event);
d3d8ac2f 532
6a0f1f6d 533 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
d23c45bf
TG
534 if (r < 0)
535 goto error;
536
aba26854
PF
537 switch (client->state) {
538 case DHCP_STATE_RENEWING:
539
e5002702 540 time_left = (client->lease->t2 - client->lease->t1) / 2;
aba26854
PF
541 if (time_left < 60)
542 time_left = 60;
d3d8ac2f 543
d23c45bf 544 next_timeout = time_now + time_left * USEC_PER_SEC;
d3d8ac2f 545
aba26854
PF
546 break;
547
3dd71400
PF
548 case DHCP_STATE_REBINDING:
549
e5002702 550 time_left = (client->lease->lifetime - client->lease->t2) / 2;
3dd71400
PF
551 if (time_left < 60)
552 time_left = 60;
553
d23c45bf 554 next_timeout = time_now + time_left * USEC_PER_SEC;
3dd71400
PF
555 break;
556
8b1243f7
PF
557 case DHCP_STATE_REBOOTING:
558 /* start over as we did not receive a timely ack or nak */
7739a40b 559 r = client_initialize(client);
eb105b96
TG
560 if (r < 0)
561 goto error;
8b1243f7 562
998d8047
TG
563 r = client_start(client);
564 if (r < 0)
565 goto error;
566 else {
567 log_dhcp_client(client, "REBOOTED");
568 return 0;
569 }
7739a40b 570
aba26854
PF
571 case DHCP_STATE_INIT:
572 case DHCP_STATE_INIT_REBOOT:
aba26854
PF
573 case DHCP_STATE_SELECTING:
574 case DHCP_STATE_REQUESTING:
575 case DHCP_STATE_BOUND:
aba26854
PF
576
577 if (client->attempt < 64)
578 client->attempt *= 2;
579
d23c45bf 580 next_timeout = time_now + (client->attempt - 1) * USEC_PER_SEC;
aba26854
PF
581
582 break;
781ca7a1
PF
583
584 case DHCP_STATE_STOPPED:
585 r = -EINVAL;
586 goto error;
aba26854
PF
587 }
588
9bf3b535 589 next_timeout += (random_u32() & 0x1fffff);
d3d8ac2f 590
2333154a
UTL
591 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
592
6a0f1f6d
LP
593 r = sd_event_add_time(client->event,
594 &client->timeout_resend,
595 CLOCK_MONOTONIC,
596 next_timeout, 10 * USEC_PER_MSEC,
597 client_timeout_resend, client);
b25ef18b
TG
598 if (r < 0)
599 goto error;
600
e5002702
TG
601 r = sd_event_source_set_priority(client->timeout_resend,
602 client->event_priority);
b25ef18b 603 if (r < 0)
e2dfc79f 604 goto error;
d3d8ac2f 605
e2dfc79f
PF
606 switch (client->state) {
607 case DHCP_STATE_INIT:
6236f49b 608 r = client_send_discover(client);
b25ef18b 609 if (r >= 0) {
e2dfc79f
PF
610 client->state = DHCP_STATE_SELECTING;
611 client->attempt = 1;
612 } else {
613 if (client->attempt >= 64)
614 goto error;
615 }
616
617 break;
618
619 case DHCP_STATE_SELECTING:
6236f49b 620 r = client_send_discover(client);
b25ef18b 621 if (r < 0 && client->attempt >= 64)
d3d8ac2f
PF
622 goto error;
623
e2dfc79f
PF
624 break;
625
8b1243f7 626 case DHCP_STATE_INIT_REBOOT:
e2dfc79f 627 case DHCP_STATE_REQUESTING:
aba26854 628 case DHCP_STATE_RENEWING:
3dd71400 629 case DHCP_STATE_REBINDING:
6236f49b 630 r = client_send_request(client);
b25ef18b 631 if (r < 0 && client->attempt >= 64)
e2dfc79f 632 goto error;
d3d8ac2f 633
8b1243f7
PF
634 if (client->state == DHCP_STATE_INIT_REBOOT)
635 client->state = DHCP_STATE_REBOOTING;
636
d23c45bf 637 client->request_sent = time_now;
51debc1e 638
d3d8ac2f
PF
639 break;
640
d3d8ac2f 641 case DHCP_STATE_REBOOTING:
d3d8ac2f 642 case DHCP_STATE_BOUND:
d3d8ac2f
PF
643
644 break;
781ca7a1
PF
645
646 case DHCP_STATE_STOPPED:
647 r = -EINVAL;
648 goto error;
d3d8ac2f
PF
649 }
650
651 return 0;
652
653error:
b25ef18b 654 client_stop(client, r);
d3d8ac2f
PF
655
656 /* Errors were dealt with when stopping the client, don't spill
657 errors into the event loop handler */
658 return 0;
659}
660
e5002702 661static int client_initialize_events(sd_dhcp_client *client,
d23c45bf 662 sd_event_io_handler_t io_callback) {
6a1cd41e
PF
663 int r;
664
b25ef18b
TG
665 assert(client);
666 assert(client->event);
667
151b9b96
LP
668 r = sd_event_add_io(client->event, &client->receive_message,
669 client->fd, EPOLLIN, io_callback,
670 client);
6a1cd41e
PF
671 if (r < 0)
672 goto error;
673
e5002702
TG
674 r = sd_event_source_set_priority(client->receive_message,
675 client->event_priority);
b25ef18b
TG
676 if (r < 0)
677 goto error;
678
2333154a
UTL
679 client->timeout_resend = sd_event_source_unref(client->timeout_resend);
680
6a0f1f6d
LP
681 r = sd_event_add_time(client->event,
682 &client->timeout_resend,
683 CLOCK_MONOTONIC,
684 0, 0,
685 client_timeout_resend, client);
b25ef18b
TG
686 if (r < 0)
687 goto error;
688
e5002702
TG
689 r = sd_event_source_set_priority(client->timeout_resend,
690 client->event_priority);
6a1cd41e
PF
691
692error:
693 if (r < 0)
694 client_stop(client, r);
695
696 return 0;
697
698}
699
0f941add
PF
700static int client_start(sd_dhcp_client *client) {
701 int r;
702
703 assert_return(client, -EINVAL);
704 assert_return(client->event, -EINVAL);
705 assert_return(client->index > 0, -EINVAL);
706 assert_return(client->fd < 0, -EBUSY);
707 assert_return(client->xid == 0, -EINVAL);
708 assert_return(client->state == DHCP_STATE_INIT ||
709 client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
710
711 client->xid = random_u32();
712
7429b07f 713 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
0f941add
PF
714 if (r < 0) {
715 client_stop(client, r);
716 return r;
717 }
0f941add 718 client->fd = r;
58ec2d3e
TG
719
720 if (client->state == DHCP_STATE_INIT) {
721 client->start_time = now(CLOCK_MONOTONIC);
722 client->secs = 0;
723 }
0f941add 724
0f941add
PF
725 return client_initialize_events(client, client_receive_message_raw);
726}
727
51debc1e 728static int client_timeout_expire(sd_event_source *s, uint64_t usec,
5ee482df 729 void *userdata) {
751246ee 730 sd_dhcp_client *client = userdata;
574cc928 731 DHCP_CLIENT_DONT_DESTROY(client);
751246ee 732
ee57a737
TG
733 log_dhcp_client(client, "EXPIRED");
734
574cc928 735 client_notify(client, DHCP_EVENT_EXPIRED);
0f941add 736
781ca7a1 737 /* lease was lost, start over if not freed or stopped in callback */
574cc928 738 if (client->state != DHCP_STATE_STOPPED) {
e5b04c8d
PF
739 client_initialize(client);
740 client_start(client);
741 }
751246ee 742
51debc1e
PF
743 return 0;
744}
745
5ee482df 746static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
3dd71400 747 sd_dhcp_client *client = userdata;
574cc928 748 DHCP_CLIENT_DONT_DESTROY(client);
3dd71400
PF
749 int r;
750
03e334a1 751 client->receive_message = sd_event_source_unref(client->receive_message);
22fc2420 752 client->fd = asynchronous_close(client->fd);
3dd71400
PF
753
754 client->state = DHCP_STATE_REBINDING;
755 client->attempt = 1;
756
7429b07f 757 r = dhcp_network_bind_raw_socket(client->index, &client->link, client->xid);
3dd71400
PF
758 if (r < 0) {
759 client_stop(client, r);
760 return 0;
761 }
3dd71400
PF
762 client->fd = r;
763
d23c45bf 764 return client_initialize_events(client, client_receive_message_raw);
51debc1e
PF
765}
766
e5002702
TG
767static int client_timeout_t1(sd_event_source *s, uint64_t usec,
768 void *userdata) {
aba26854 769 sd_dhcp_client *client = userdata;
574cc928 770 DHCP_CLIENT_DONT_DESTROY(client);
aba26854
PF
771 int r;
772
773 client->state = DHCP_STATE_RENEWING;
774 client->attempt = 1;
775
085cabf2 776 r = dhcp_network_bind_udp_socket(client->lease->address,
080ab276 777 DHCP_PORT_CLIENT);
6a1cd41e
PF
778 if (r < 0) {
779 client_stop(client, r);
780 return 0;
781 }
aba26854
PF
782
783 client->fd = r;
aba26854 784
d23c45bf 785 return client_initialize_events(client, client_receive_message_udp);
51debc1e
PF
786}
787
e5002702
TG
788static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
789 size_t len) {
a6cc569e 790 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 791 int r;
3e3d8f78 792
a6cc569e
TG
793 r = dhcp_lease_new(&lease);
794 if (r < 0)
795 return r;
8c00042c 796
e5002702 797 r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
9e64dd72
TG
798 if (r != DHCP_OFFER) {
799 log_dhcp_client(client, "receieved message was not an OFFER, ignoring");
5ee482df 800 return -ENOMSG;
9e64dd72 801 }
8c00042c 802
8e34a618
TG
803 lease->next_server = offer->siaddr;
804
e5002702 805 lease->address = offer->yiaddr;
8c00042c
PF
806
807 if (lease->address == INADDR_ANY ||
808 lease->server_address == INADDR_ANY ||
9e64dd72
TG
809 lease->lifetime == 0) {
810 log_dhcp_client(client, "receieved lease lacks address, server "
811 "address or lease lifetime, ignoring");
5ee482df 812 return -ENOMSG;
9e64dd72
TG
813 }
814
815 if (lease->subnet_mask == INADDR_ANY) {
816 r = dhcp_lease_set_default_subnet_mask(lease);
817 if (r < 0) {
818 log_dhcp_client(client, "receieved lease lacks subnet "
819 "mask, and a fallback one can not be "
820 "generated, ignoring");
821 return -ENOMSG;
822 }
823 }
8c00042c 824
6e00a806 825 sd_dhcp_lease_unref(client->lease);
8c00042c 826 client->lease = lease;
5ee482df 827 lease = NULL;
8c00042c 828
ee57a737
TG
829 log_dhcp_client(client, "OFFER");
830
8c00042c 831 return 0;
8c00042c
PF
832}
833
e5002702
TG
834static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
835 size_t len) {
a6cc569e 836 _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
5ee482df 837 int r;
3e3d8f78 838
a6cc569e
TG
839 r = dhcp_lease_new(&lease);
840 if (r < 0)
841 return r;
3e3d8f78 842
e5002702 843 r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
ee57a737
TG
844 if (r == DHCP_NAK) {
845 log_dhcp_client(client, "NAK");
5ee482df 846 return DHCP_EVENT_NO_LEASE;
ee57a737 847 }
3e3d8f78 848
9e64dd72
TG
849 if (r != DHCP_ACK) {
850 log_dhcp_client(client, "receieved message was not an ACK, ignoring");
5ee482df 851 return -ENOMSG;
9e64dd72 852 }
3e3d8f78 853
8e34a618
TG
854 lease->next_server = ack->siaddr;
855
e5002702 856 lease->address = ack->yiaddr;
3e3d8f78
PF
857
858 if (lease->address == INADDR_ANY ||
859 lease->server_address == INADDR_ANY ||
9e64dd72
TG
860 lease->lifetime == 0) {
861 log_dhcp_client(client, "receieved lease lacks address, server "
862 "address or lease lifetime, ignoring");
5ee482df 863 return -ENOMSG;
9e64dd72
TG
864 }
865
866 if (lease->subnet_mask == INADDR_ANY) {
867 r = dhcp_lease_set_default_subnet_mask(lease);
868 if (r < 0) {
869 log_dhcp_client(client, "receieved lease lacks subnet "
870 "mask, and a fallback one can not be "
871 "generated, ignoring");
872 return -ENOMSG;
873 }
874 }
3e3d8f78
PF
875
876 r = DHCP_EVENT_IP_ACQUIRE;
877 if (client->lease) {
878 if (client->lease->address != lease->address ||
879 client->lease->subnet_mask != lease->subnet_mask ||
880 client->lease->router != lease->router) {
881 r = DHCP_EVENT_IP_CHANGE;
68ceb9df
PF
882 } else
883 r = DHCP_EVENT_RENEW;
3e3d8f78 884
a6cc569e 885 client->lease = sd_dhcp_lease_unref(client->lease);
3e3d8f78
PF
886 }
887
888 client->lease = lease;
5ee482df 889 lease = NULL;
3e3d8f78 890
ee57a737
TG
891 log_dhcp_client(client, "ACK");
892
3e3d8f78
PF
893 return r;
894}
895
022446ad
TG
896static uint64_t client_compute_timeout(sd_dhcp_client *client,
897 uint32_t lifetime, double factor) {
898 assert(client);
899 assert(client->request_sent);
900 assert(lifetime);
901
902 return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) +
9bf3b535 903 + (random_u32() & 0x1fffff);
51debc1e
PF
904}
905
022446ad
TG
906static int client_set_lease_timeouts(sd_dhcp_client *client) {
907 usec_t time_now;
908 uint64_t lifetime_timeout;
909 uint64_t t2_timeout;
910 uint64_t t1_timeout;
911 char time_string[FORMAT_TIMESPAN_MAX];
5ee482df 912 int r;
51debc1e 913
b25ef18b
TG
914 assert(client);
915 assert(client->event);
022446ad
TG
916 assert(client->lease);
917 assert(client->lease->lifetime);
51debc1e 918
aba26854
PF
919 client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
920 client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
921 client->timeout_expire = sd_event_source_unref(client->timeout_expire);
922
022446ad
TG
923 /* don't set timers for infinite leases */
924 if (client->lease->lifetime == 0xffffffff)
925 return 0;
51debc1e 926
6a0f1f6d 927 r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
022446ad
TG
928 if (r < 0)
929 return r;
930 assert(client->request_sent <= time_now);
931
932 /* convert the various timeouts from relative (secs) to absolute (usecs) */
933 lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1);
934 if (client->lease->t1 && client->lease->t2) {
935 /* both T1 and T2 are given */
936 if (client->lease->t1 < client->lease->t2 &&
937 client->lease->t2 < client->lease->lifetime) {
938 /* they are both valid */
939 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
940 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
941 } else {
942 /* discard both */
943 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
944 client->lease->t2 = (client->lease->lifetime * 7) / 8;
945 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
946 client->lease->t1 = client->lease->lifetime / 2;
947 }
948 } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) {
949 /* only T2 is given, and it is valid */
950 t2_timeout = client_compute_timeout(client, client->lease->t2, 1);
951 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
952 client->lease->t1 = client->lease->lifetime / 2;
953 if (t2_timeout <= t1_timeout) {
954 /* the computed T1 would be invalid, so discard T2 */
955 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
956 client->lease->t2 = (client->lease->lifetime * 7) / 8;
957 }
958 } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) {
959 /* only T1 is given, and it is valid */
960 t1_timeout = client_compute_timeout(client, client->lease->t1, 1);
961 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
962 client->lease->t2 = (client->lease->lifetime * 7) / 8;
963 if (t2_timeout <= t1_timeout) {
964 /* the computed T2 would be invalid, so discard T1 */
965 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
966 client->lease->t2 = client->lease->lifetime / 2;
967 }
968 } else {
969 /* fall back to the default timeouts */
970 t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5);
971 client->lease->t1 = client->lease->lifetime / 2;
972 t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0);
973 client->lease->t2 = (client->lease->lifetime * 7) / 8;
974 }
51debc1e 975
022446ad 976 /* arm lifetime timeout */
6a0f1f6d
LP
977 r = sd_event_add_time(client->event, &client->timeout_expire,
978 CLOCK_MONOTONIC,
979 lifetime_timeout, 10 * USEC_PER_MSEC,
980 client_timeout_expire, client);
5ee482df
TG
981 if (r < 0)
982 return r;
51debc1e 983
022446ad 984 r = sd_event_source_set_priority(client->timeout_expire,
e5002702 985 client->event_priority);
b25ef18b
TG
986 if (r < 0)
987 return r;
988
022446ad
TG
989 log_dhcp_client(client, "lease expires in %s",
990 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
991 lifetime_timeout - time_now, 0));
51debc1e 992
022446ad
TG
993 /* don't arm earlier timeouts if this has already expired */
994 if (lifetime_timeout <= time_now)
995 return 0;
51debc1e 996
022446ad 997 /* arm T2 timeout */
6a0f1f6d
LP
998 r = sd_event_add_time(client->event,
999 &client->timeout_t2,
1000 CLOCK_MONOTONIC,
1001 t2_timeout,
1002 10 * USEC_PER_MSEC,
1003 client_timeout_t2, client);
5ee482df
TG
1004 if (r < 0)
1005 return r;
51debc1e 1006
e5002702
TG
1007 r = sd_event_source_set_priority(client->timeout_t2,
1008 client->event_priority);
b25ef18b
TG
1009 if (r < 0)
1010 return r;
1011
022446ad
TG
1012 log_dhcp_client(client, "T2 expires in %s",
1013 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1014 t2_timeout - time_now, 0));
1015
1016 /* don't arm earlier timeout if this has already expired */
1017 if (t2_timeout <= time_now)
1018 return 0;
51debc1e 1019
022446ad 1020 /* arm T1 timeout */
6a0f1f6d
LP
1021 r = sd_event_add_time(client->event,
1022 &client->timeout_t1,
1023 CLOCK_MONOTONIC,
1024 t1_timeout, 10 * USEC_PER_MSEC,
1025 client_timeout_t1, client);
5ee482df
TG
1026 if (r < 0)
1027 return r;
51debc1e 1028
022446ad 1029 r = sd_event_source_set_priority(client->timeout_t1,
e5002702 1030 client->event_priority);
b25ef18b
TG
1031 if (r < 0)
1032 return r;
1033
022446ad
TG
1034 log_dhcp_client(client, "T1 expires in %s",
1035 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
1036 t1_timeout - time_now, 0));
1037
51debc1e
PF
1038 return 0;
1039}
1040
e5002702 1041static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
022446ad 1042 int len) {
574cc928 1043 DHCP_CLIENT_DONT_DESTROY(client);
e5002702 1044 int r = 0, notify_event = 0;
8c00042c 1045
b25ef18b
TG
1046 assert(client);
1047 assert(client->event);
e5002702 1048 assert(message);
b25ef18b 1049
3b7ca119
TG
1050 if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
1051 log_dhcp_client(client, "not a DHCP message: ignoring");
1052 return 0;
1053 }
1054
06b44be7
TG
1055 if (message->op != BOOTREPLY) {
1056 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
1057 return 0;
1058 }
1059
1060 if (be32toh(message->xid) != client->xid) {
1061 log_dhcp_client(client, "received xid (%u) does not match "
1062 "expected (%u): ignoring",
1063 be32toh(message->xid), client->xid);
1064 return 0;
1065 }
e2dfc79f 1066
3b7ca119
TG
1067 if (message->htype != ARPHRD_ETHER || message->hlen != ETHER_ADDR_LEN) {
1068 log_dhcp_client(client, "not an ethernet packet");
1069 return 0;
1070 }
1071
715c6a9a
PF
1072 if (memcmp(&message->chaddr[0], &client->client_id.mac_addr,
1073 ETH_ALEN)) {
06b44be7
TG
1074 log_dhcp_client(client, "received chaddr does not match "
1075 "expected: ignoring");
9fadd4ca 1076 return 0;
06b44be7 1077 }
8c00042c 1078
8c00042c
PF
1079 switch (client->state) {
1080 case DHCP_STATE_SELECTING:
1081
e5002702
TG
1082 r = client_handle_offer(client, message, len);
1083 if (r >= 0) {
8c00042c 1084
8c00042c
PF
1085 client->timeout_resend =
1086 sd_event_source_unref(client->timeout_resend);
1087
1088 client->state = DHCP_STATE_REQUESTING;
e2dfc79f
PF
1089 client->attempt = 1;
1090
6a0f1f6d
LP
1091 r = sd_event_add_time(client->event,
1092 &client->timeout_resend,
1093 CLOCK_MONOTONIC,
1094 0, 0,
1095 client_timeout_resend, client);
3e3d8f78 1096 if (r < 0)
e2dfc79f 1097 goto error;
b25ef18b 1098
e5002702
TG
1099 r = sd_event_source_set_priority(client->timeout_resend,
1100 client->event_priority);
b25ef18b
TG
1101 if (r < 0)
1102 goto error;
9e64dd72
TG
1103 } else if (r == -ENOMSG)
1104 /* invalid message, let's ignore it */
1105 return 0;
8c00042c
PF
1106
1107 break;
1108
8b1243f7 1109 case DHCP_STATE_REBOOTING:
3e3d8f78 1110 case DHCP_STATE_REQUESTING:
aba26854 1111 case DHCP_STATE_RENEWING:
3dd71400 1112 case DHCP_STATE_REBINDING:
aba26854 1113
e5002702 1114 r = client_handle_ack(client, message, len);
8b1243f7
PF
1115 if (r == DHCP_EVENT_NO_LEASE) {
1116
1117 client->timeout_resend =
1118 sd_event_source_unref(client->timeout_resend);
1119
1120 if (client->state == DHCP_STATE_REBOOTING) {
1121 r = client_initialize(client);
1122 if (r < 0)
1123 goto error;
1124
1125 r = client_start(client);
1126 if (r < 0)
1127 goto error;
998d8047
TG
1128
1129 log_dhcp_client(client, "REBOOTED");
8b1243f7
PF
1130 }
1131
3e3d8f78 1132 goto error;
9e64dd72 1133 } else if (r >= 0) {
3e3d8f78
PF
1134 client->timeout_resend =
1135 sd_event_source_unref(client->timeout_resend);
1136
8b1243f7
PF
1137 if (IN_SET(client->state, DHCP_STATE_REQUESTING,
1138 DHCP_STATE_REBOOTING))
aba26854
PF
1139 notify_event = DHCP_EVENT_IP_ACQUIRE;
1140 else if (r != DHCP_EVENT_IP_ACQUIRE)
1141 notify_event = r;
1142
3e3d8f78
PF
1143 client->state = DHCP_STATE_BOUND;
1144 client->attempt = 1;
1145
1146 client->last_addr = client->lease->address;
1147
022446ad 1148 r = client_set_lease_timeouts(client);
aba26854 1149 if (r < 0)
51debc1e
PF
1150 goto error;
1151
e5b04c8d 1152 if (notify_event) {
574cc928
TG
1153 client_notify(client, notify_event);
1154 if (client->state == DHCP_STATE_STOPPED)
e5b04c8d
PF
1155 return 0;
1156 }
3e3d8f78 1157
3e3d8f78
PF
1158 client->receive_message =
1159 sd_event_source_unref(client->receive_message);
22fc2420 1160 client->fd = asynchronous_close(client->fd);
9e64dd72
TG
1161 } else if (r == -ENOMSG)
1162 /* invalid message, let's ignore it */
1163 return 0;
97b9372d 1164
3e3d8f78
PF
1165 break;
1166
8c00042c
PF
1167 case DHCP_STATE_INIT:
1168 case DHCP_STATE_INIT_REBOOT:
8c00042c 1169 case DHCP_STATE_BOUND:
8c00042c
PF
1170
1171 break;
781ca7a1
PF
1172
1173 case DHCP_STATE_STOPPED:
1174 r = -EINVAL;
1175 goto error;
8c00042c
PF
1176 }
1177
1178error:
97b9372d 1179 if (r < 0 || r == DHCP_EVENT_NO_LEASE)
e5b04c8d 1180 client_stop(client, r);
e2dfc79f 1181
e5b04c8d 1182 return r;
8c00042c
PF
1183}
1184
8f61afd8 1185static int client_receive_message_udp(sd_event_source *s, int fd,
e5002702
TG
1186 uint32_t revents, void *userdata) {
1187 sd_dhcp_client *client = userdata;
5266a81e
TG
1188 _cleanup_free_ DHCPMessage *message = NULL;
1189 int buflen = 0, len, r;
e5002702
TG
1190
1191 assert(s);
1192 assert(client);
e5002702 1193
5266a81e 1194 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1195 if (r < 0)
1196 return r;
1197
1198 if (buflen < 0)
1199 /* this can't be right */
1200 return -EIO;
5266a81e
TG
1201
1202 message = malloc0(buflen);
1203 if (!message)
1204 return -ENOMEM;
1205
1206 len = read(fd, message, buflen);
0c79c68d
TG
1207 if (len < 0) {
1208 log_dhcp_client(client, "could not receive message from UDP "
590b6b91 1209 "socket: %m");
0c79c68d
TG
1210 return 0;
1211 } else if ((size_t)len < sizeof(DHCPMessage))
e5002702
TG
1212 return 0;
1213
022446ad 1214 return client_handle_message(client, message, len);
e5002702
TG
1215}
1216
8f61afd8 1217static int client_receive_message_raw(sd_event_source *s, int fd,
e5002702
TG
1218 uint32_t revents, void *userdata) {
1219 sd_dhcp_client *client = userdata;
5266a81e 1220 _cleanup_free_ DHCPPacket *packet = NULL;
55dab2ed
TG
1221 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
1222 struct iovec iov = {};
1223 struct msghdr msg = {
1224 .msg_iov = &iov,
1225 .msg_iovlen = 1,
1226 .msg_control = cmsgbuf,
1227 .msg_controllen = sizeof(cmsgbuf),
1228 };
1229 struct cmsghdr *cmsg;
1230 bool checksum = true;
1231 int buflen = 0, len, r;
e5002702
TG
1232
1233 assert(s);
1234 assert(client);
e5002702 1235
5266a81e 1236 r = ioctl(fd, FIONREAD, &buflen);
23289745
TG
1237 if (r < 0)
1238 return r;
1239
1240 if (buflen < 0)
1241 /* this can't be right */
1242 return -EIO;
5266a81e
TG
1243
1244 packet = malloc0(buflen);
1245 if (!packet)
1246 return -ENOMEM;
1247
55dab2ed
TG
1248 iov.iov_base = packet;
1249 iov.iov_len = buflen;
1250
1251 len = recvmsg(fd, &msg, 0);
1252 if (len < 0) {
1253 log_dhcp_client(client, "could not receive message from raw "
590b6b91 1254 "socket: %m");
e5002702 1255 return 0;
0c79c68d
TG
1256 } else if ((size_t)len < sizeof(DHCPPacket))
1257 return 0;
55dab2ed
TG
1258
1259 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
48a4612e
TG
1260 if (cmsg->cmsg_level == SOL_PACKET &&
1261 cmsg->cmsg_type == PACKET_AUXDATA &&
1262 cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
1263 struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
55dab2ed
TG
1264
1265 checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
1266 break;
1267 }
1268 }
e5002702 1269
55dab2ed 1270 r = dhcp_packet_verify_headers(packet, len, checksum);
ac4f16ab 1271 if (r < 0)
9fadd4ca 1272 return 0;
e5002702
TG
1273
1274 len -= DHCP_IP_UDP_SIZE;
1275
022446ad 1276 return client_handle_message(client, &packet->dhcp, len);
e5002702
TG
1277}
1278
5ee482df 1279int sd_dhcp_client_start(sd_dhcp_client *client) {
6a1cd41e 1280 int r;
d3d8ac2f 1281
46a66b79 1282 assert_return(client, -EINVAL);
46a66b79 1283
0f941add
PF
1284 r = client_initialize(client);
1285 if (r < 0)
6a1cd41e 1286 return r;
8c00042c 1287
0f941add
PF
1288 if (client->last_addr)
1289 client->state = DHCP_STATE_INIT_REBOOT;
d3d8ac2f 1290
998d8047
TG
1291 r = client_start(client);
1292 if (r >= 0)
63175195
TG
1293 log_dhcp_client(client, "STARTED on ifindex %u with address %s",
1294 client->index,
1295 ether_ntoa(&client->client_id.mac_addr));
998d8047
TG
1296
1297 return r;
46a66b79
PF
1298}
1299
5ee482df 1300int sd_dhcp_client_stop(sd_dhcp_client *client) {
574cc928
TG
1301 DHCP_CLIENT_DONT_DESTROY(client);
1302
e5b04c8d
PF
1303 assert_return(client, -EINVAL);
1304
574cc928
TG
1305 client_stop(client, DHCP_EVENT_STOP);
1306 client->state = DHCP_STATE_STOPPED;
e5b04c8d
PF
1307
1308 return 0;
bbdf06d9
PF
1309}
1310
e5002702
TG
1311int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
1312 int priority) {
b25ef18b
TG
1313 int r;
1314
1315 assert_return(client, -EINVAL);
1316 assert_return(!client->event, -EBUSY);
1317
1318 if (event)
1319 client->event = sd_event_ref(event);
1320 else {
1321 r = sd_event_default(&client->event);
1322 if (r < 0)
1323 return 0;
1324 }
1325
1326 client->event_priority = priority;
1327
1328 return 0;
1329}
1330
1331int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1332 assert_return(client, -EINVAL);
1333
1334 client->event = sd_event_unref(client->event);
1335
1336 return 0;
1337}
1338
1339sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1340 if (!client)
1341 return NULL;
1342
1343 return client->event;
1344}
1345
e5b04c8d
PF
1346sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) {
1347 if (client)
1348 assert_se(REFCNT_INC(client->n_ref) >= 2);
1349
1350 return client;
1351}
1352
1353sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
1354 if (client && REFCNT_DEC(client->n_ref) <= 0) {
574cc928 1355 log_dhcp_client(client, "FREE");
d2fe46b5 1356
e5b04c8d
PF
1357 client_initialize(client);
1358
1359 client->receive_message =
1360 sd_event_source_unref(client->receive_message);
1361
1362 sd_dhcp_client_detach_event(client);
1363
6e00a806
ZJS
1364 sd_dhcp_lease_unref(client->lease);
1365
e5b04c8d
PF
1366 free(client->req_opts);
1367 free(client);
e5b04c8d 1368 }
d2fe46b5 1369
574cc928 1370 return NULL;
d2fe46b5
PF
1371}
1372
b25ef18b 1373int sd_dhcp_client_new(sd_dhcp_client **ret) {
574cc928 1374 _cleanup_dhcp_client_unref_ sd_dhcp_client *client = NULL;
011feef8 1375
b25ef18b 1376 assert_return(ret, -EINVAL);
d3d8ac2f 1377
011feef8
PF
1378 client = new0(sd_dhcp_client, 1);
1379 if (!client)
b25ef18b 1380 return -ENOMEM;
011feef8 1381
e5b04c8d 1382 client->n_ref = REFCNT_INIT;
011feef8
PF
1383 client->state = DHCP_STATE_INIT;
1384 client->index = -1;
8c00042c 1385 client->fd = -1;
e2dfc79f 1386 client->attempt = 1;
011feef8
PF
1387
1388 client->req_opts_size = ELEMENTSOF(default_req_opts);
1389
1390 client->req_opts = memdup(default_req_opts, client->req_opts_size);
b25ef18b
TG
1391 if (!client->req_opts)
1392 return -ENOMEM;
1393
1394 *ret = client;
1395 client = NULL;
011feef8 1396
b25ef18b 1397 return 0;
011feef8 1398}