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