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