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