chiark / gitweb /
sd-dhcp-lease: add sd_dhcp_lease_get_next_server()
[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 <sys/param.h>
26 #include <sys/ioctl.h>
27
28 #include "util.h"
29 #include "list.h"
30
31 #include "dhcp-protocol.h"
32 #include "dhcp-internal.h"
33 #include "dhcp-lease-internal.h"
34 #include "sd-dhcp-client.h"
35
36 struct sd_dhcp_client {
37         DHCPState state;
38         sd_event *event;
39         int event_priority;
40         sd_event_source *timeout_resend;
41         int index;
42         int fd;
43         union sockaddr_union link;
44         sd_event_source *receive_message;
45         uint8_t *req_opts;
46         size_t req_opts_allocated;
47         size_t req_opts_size;
48         be32_t last_addr;
49         struct ether_addr mac_addr;
50         uint32_t xid;
51         usec_t start_time;
52         uint16_t secs;
53         unsigned int attempt;
54         usec_t request_sent;
55         sd_event_source *timeout_t1;
56         sd_event_source *timeout_t2;
57         sd_event_source *timeout_expire;
58         sd_dhcp_client_cb_t cb;
59         void *userdata;
60         sd_dhcp_lease *lease;
61 };
62
63 static const uint8_t default_req_opts[] = {
64         DHCP_OPTION_SUBNET_MASK,
65         DHCP_OPTION_ROUTER,
66         DHCP_OPTION_HOST_NAME,
67         DHCP_OPTION_DOMAIN_NAME,
68         DHCP_OPTION_DOMAIN_NAME_SERVER,
69         DHCP_OPTION_NTP_SERVER,
70 };
71
72 static int client_receive_message_raw(sd_event_source *s, int fd,
73                                       uint32_t revents, void *userdata);
74 static int client_receive_message_udp(sd_event_source *s, int fd,
75                                       uint32_t revents, void *userdata);
76
77 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
78                                 void *userdata) {
79         assert_return(client, -EINVAL);
80
81         client->cb = cb;
82         client->userdata = userdata;
83
84         return 0;
85 }
86
87 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
88         size_t i;
89
90         assert_return(client, -EINVAL);
91         assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
92
93         switch(option) {
94         case DHCP_OPTION_PAD:
95         case DHCP_OPTION_OVERLOAD:
96         case DHCP_OPTION_MESSAGE_TYPE:
97         case DHCP_OPTION_PARAMETER_REQUEST_LIST:
98         case DHCP_OPTION_END:
99                 return -EINVAL;
100
101         default:
102                 break;
103         }
104
105         for (i = 0; i < client->req_opts_size; i++)
106                 if (client->req_opts[i] == option)
107                         return -EEXIST;
108
109         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
110                             client->req_opts_size + 1))
111                 return -ENOMEM;
112
113         client->req_opts[client->req_opts_size++] = option;
114
115         return 0;
116 }
117
118 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
119                                        const struct in_addr *last_addr) {
120         assert_return(client, -EINVAL);
121         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
122
123         if (last_addr)
124                 client->last_addr = last_addr->s_addr;
125         else
126                 client->last_addr = INADDR_ANY;
127
128         return 0;
129 }
130
131 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
132         assert_return(client, -EINVAL);
133         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
134         assert_return(interface_index >= -1, -EINVAL);
135
136         client->index = interface_index;
137
138         return 0;
139 }
140
141 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
142                            const struct ether_addr *addr) {
143         assert_return(client, -EINVAL);
144         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
145
146         memcpy(&client->mac_addr, addr, ETH_ALEN);
147
148         return 0;
149 }
150
151 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
152         assert_return(client, -EINVAL);
153         assert_return(ret, -EINVAL);
154
155         if (client->state != DHCP_STATE_BOUND &&
156             client->state != DHCP_STATE_RENEWING &&
157             client->state != DHCP_STATE_REBINDING)
158                 return -EADDRNOTAVAIL;
159
160         *ret = sd_dhcp_lease_ref(client->lease);
161
162         return 0;
163 }
164
165 static int client_notify(sd_dhcp_client *client, int event) {
166         if (client->cb)
167                 client->cb(client, event, client->userdata);
168
169         return 0;
170 }
171
172 static int client_stop(sd_dhcp_client *client, int error) {
173         assert_return(client, -EINVAL);
174
175         client->receive_message =
176                 sd_event_source_unref(client->receive_message);
177
178         if (client->fd >= 0)
179                 close(client->fd);
180         client->fd = -1;
181
182         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
183
184         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
185         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
186         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
187
188         client->attempt = 1;
189
190         client_notify(client, error);
191
192         client->start_time = 0;
193         client->secs = 0;
194         client->state = DHCP_STATE_INIT;
195
196         if (client->lease)
197                 client->lease = sd_dhcp_lease_unref(client->lease);
198
199         log_dhcp_client(client, "STOPPED");
200
201         return 0;
202 }
203
204 static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
205                                uint8_t type, uint16_t secs, uint8_t **opt,
206                                size_t *optlen) {
207         int r;
208
209         assert(secs);
210
211         r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
212                               optlen);
213         if (r < 0)
214                 return r;
215
216         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
217            refuse to issue an DHCP lease if 'secs' is set to zero */
218         message->secs = htobe16(secs);
219
220         memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
221
222         if (client->state == DHCP_STATE_RENEWING ||
223             client->state == DHCP_STATE_REBINDING)
224                 message->ciaddr = client->lease->address;
225
226         /* Some DHCP servers will refuse to issue an DHCP lease if the Client
227            Identifier option is not set */
228         r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
229                                ETH_ALEN, &client->mac_addr);
230         if (r < 0)
231                 return r;
232
233         if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
234                 be16_t max_size;
235
236                 r = dhcp_option_append(opt, optlen,
237                                        DHCP_OPTION_PARAMETER_REQUEST_LIST,
238                                        client->req_opts_size,
239                                        client->req_opts);
240                 if (r < 0)
241                         return r;
242
243                 /* Some DHCP servers will send bigger DHCP packets than the
244                    defined default size unless the Maximum Messge Size option
245                    is explicitely set */
246                 max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
247                                    DHCP_MIN_OPTIONS_SIZE);
248                 r = dhcp_option_append(opt, optlen,
249                                        DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
250                                        2, &max_size);
251                 if (r < 0)
252                         return r;
253         }
254
255         return 0;
256 }
257
258 static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
259         int err = 0;
260         _cleanup_free_ DHCPPacket *discover;
261         size_t optlen, len;
262         uint8_t *opt;
263
264         optlen = DHCP_MIN_OPTIONS_SIZE;
265         len = sizeof(DHCPPacket) + optlen;
266
267         discover = malloc0(len);
268
269         if (!discover)
270                 return -ENOMEM;
271
272         err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
273                                   secs, &opt, &optlen);
274         if (err < 0)
275                 return err;
276
277         if (client->last_addr != INADDR_ANY) {
278                 err = dhcp_option_append(&opt, &optlen,
279                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
280                                          4, &client->last_addr);
281                 if (err < 0)
282                         return err;
283         }
284
285         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
286         if (err < 0)
287                 return err;
288
289         dhcp_packet_append_ip_headers(discover, len);
290
291         err = dhcp_network_send_raw_socket(client->fd, &client->link,
292                                            discover, len);
293
294         log_dhcp_client(client, "DISCOVER");
295
296         return err;
297 }
298
299 static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
300         _cleanup_free_ DHCPPacket *request;
301         size_t optlen, len;
302         int err;
303         uint8_t *opt;
304
305         optlen = DHCP_MIN_OPTIONS_SIZE;
306         len = sizeof(DHCPPacket) + optlen;
307
308         request = malloc0(len);
309         if (!request)
310                 return -ENOMEM;
311
312         err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
313                                   &opt, &optlen);
314         if (err < 0)
315                 return err;
316
317         if (client->state == DHCP_STATE_REQUESTING) {
318                 err = dhcp_option_append(&opt, &optlen,
319                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
320                                          4, &client->lease->address);
321                 if (err < 0)
322                         return err;
323
324                 err = dhcp_option_append(&opt, &optlen,
325                                          DHCP_OPTION_SERVER_IDENTIFIER,
326                                          4, &client->lease->server_address);
327                 if (err < 0)
328                         return err;
329         }
330
331         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
332         if (err < 0)
333                 return err;
334
335         if (client->state == DHCP_STATE_RENEWING) {
336                 err = dhcp_network_send_udp_socket(client->fd,
337                                                    client->lease->server_address,
338                                                    DHCP_PORT_SERVER,
339                                                    &request->dhcp,
340                                                    len - DHCP_IP_UDP_SIZE);
341         } else {
342                 dhcp_packet_append_ip_headers(request, len);
343
344                 err = dhcp_network_send_raw_socket(client->fd, &client->link,
345                                                    request, len);
346         }
347
348         log_dhcp_client(client, "REQUEST");
349
350         return err;
351 }
352
353 static uint16_t client_update_secs(sd_dhcp_client *client, usec_t time_now)
354 {
355         client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
356
357         return client->secs;
358 }
359
360 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
361                                  void *userdata) {
362         sd_dhcp_client *client = userdata;
363         usec_t next_timeout = 0;
364         uint32_t time_left;
365         int r = 0;
366
367         assert(s);
368         assert(client);
369         assert(client->event);
370
371         switch (client->state) {
372         case DHCP_STATE_RENEWING:
373
374                 time_left = (client->lease->t2 - client->lease->t1) / 2;
375                 if (time_left < 60)
376                         time_left = 60;
377
378                 next_timeout = usec + time_left * USEC_PER_SEC;
379
380                 break;
381
382         case DHCP_STATE_REBINDING:
383
384                 time_left = (client->lease->lifetime - client->lease->t2) / 2;
385                 if (time_left < 60)
386                         time_left = 60;
387
388                 next_timeout = usec + time_left * USEC_PER_SEC;
389                 break;
390
391         case DHCP_STATE_INIT:
392         case DHCP_STATE_INIT_REBOOT:
393         case DHCP_STATE_REBOOTING:
394         case DHCP_STATE_SELECTING:
395         case DHCP_STATE_REQUESTING:
396         case DHCP_STATE_BOUND:
397
398                 if (client->attempt < 64)
399                         client->attempt *= 2;
400
401                 next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
402
403                 break;
404         }
405
406         next_timeout += (random_u32() & 0x1fffff);
407
408         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
409
410         r = sd_event_add_monotonic(client->event,
411                                      &client->timeout_resend,
412                                      next_timeout,
413                                      10 * USEC_PER_MSEC,
414                                      client_timeout_resend, client);
415         if (r < 0)
416                 goto error;
417
418         r = sd_event_source_set_priority(client->timeout_resend,
419                                          client->event_priority);
420         if (r < 0)
421                 goto error;
422
423         switch (client->state) {
424         case DHCP_STATE_INIT:
425
426                 client_update_secs(client, usec);
427
428                 r = client_send_discover(client, client->secs);
429                 if (r >= 0) {
430                         client->state = DHCP_STATE_SELECTING;
431                         client->attempt = 1;
432                 } else {
433                         if (client->attempt >= 64)
434                                 goto error;
435                 }
436
437                 break;
438
439         case DHCP_STATE_SELECTING:
440                 client_update_secs(client, usec);
441
442                 r = client_send_discover(client, client->secs);
443                 if (r < 0 && client->attempt >= 64)
444                         goto error;
445
446                 break;
447
448         case DHCP_STATE_REQUESTING:
449         case DHCP_STATE_RENEWING:
450         case DHCP_STATE_REBINDING:
451                 r = client_send_request(client, client->secs);
452                 if (r < 0 && client->attempt >= 64)
453                          goto error;
454
455                 client->request_sent = usec;
456
457                 break;
458
459         case DHCP_STATE_INIT_REBOOT:
460         case DHCP_STATE_REBOOTING:
461         case DHCP_STATE_BOUND:
462
463                 break;
464         }
465
466         return 0;
467
468 error:
469         client_stop(client, r);
470
471         /* Errors were dealt with when stopping the client, don't spill
472            errors into the event loop handler */
473         return 0;
474 }
475
476 static int client_initialize_events(sd_dhcp_client *client,
477                                     sd_event_io_handler_t io_callback,
478                                     usec_t usec) {
479         int r;
480
481         assert(client);
482         assert(client->event);
483
484         r = sd_event_add_io(client->event, &client->receive_message,
485                             client->fd, EPOLLIN, io_callback,
486                             client);
487         if (r < 0)
488                 goto error;
489
490         r = sd_event_source_set_priority(client->receive_message,
491                                          client->event_priority);
492         if (r < 0)
493                 goto error;
494
495         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
496
497         r = sd_event_add_monotonic(client->event,
498                                    &client->timeout_resend,
499                                    usec, 0,
500                                    client_timeout_resend, client);
501         if (r < 0)
502                 goto error;
503
504         r = sd_event_source_set_priority(client->timeout_resend,
505                                          client->event_priority);
506
507 error:
508         if (r < 0)
509                 client_stop(client, r);
510
511         return 0;
512
513 }
514
515 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
516                                  void *userdata) {
517         sd_dhcp_client *client = userdata;
518
519         log_dhcp_client(client, "EXPIRED");
520
521         client_stop(client, DHCP_EVENT_EXPIRED);
522
523         return 0;
524 }
525
526 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
527         sd_dhcp_client *client = userdata;
528         int r;
529
530         if (client->fd >= 0) {
531                 client->receive_message =
532                         sd_event_source_unref(client->receive_message);
533                 close(client->fd);
534                 client->fd = -1;
535         }
536
537         client->state = DHCP_STATE_REBINDING;
538         client->attempt = 1;
539
540         r = dhcp_network_bind_raw_socket(client->index, &client->link);
541         if (r < 0) {
542                 client_stop(client, r);
543                 return 0;
544         }
545
546         client->fd = r;
547
548         log_dhcp_client(client, "TIMEOUT T2");
549
550         return client_initialize_events(client, client_receive_message_raw,
551                                         usec);
552 }
553
554 static int client_timeout_t1(sd_event_source *s, uint64_t usec,
555                              void *userdata) {
556         sd_dhcp_client *client = userdata;
557         int r;
558
559         client->state = DHCP_STATE_RENEWING;
560         client->attempt = 1;
561
562         r = dhcp_network_bind_udp_socket(client->index,
563                                          client->lease->address,
564                                          DHCP_PORT_CLIENT);
565         if (r < 0) {
566                 client_stop(client, r);
567                 return 0;
568         }
569
570         client->fd = r;
571
572         log_dhcp_client(client, "TIMEOUT T1");
573
574         return client_initialize_events(client, client_receive_message_udp, usec);
575 }
576
577 static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer,
578                                size_t len) {
579         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
580         int r;
581
582         r = dhcp_lease_new(&lease);
583         if (r < 0)
584                 return r;
585
586         r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease);
587         if (r != DHCP_OFFER)
588                 return -ENOMSG;
589
590         lease->next_server = offer->siaddr;
591
592         lease->address = offer->yiaddr;
593
594         if (lease->address == INADDR_ANY ||
595             lease->server_address == INADDR_ANY ||
596             lease->subnet_mask == INADDR_ANY ||
597             lease->lifetime == 0)
598                 return -ENOMSG;
599
600         client->lease = lease;
601         lease = NULL;
602
603         log_dhcp_client(client, "OFFER");
604
605         return 0;
606 }
607
608 static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
609                              size_t len) {
610         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
611         int r;
612
613         r = dhcp_lease_new(&lease);
614         if (r < 0)
615                 return r;
616
617         r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease);
618         if (r == DHCP_NAK) {
619                 log_dhcp_client(client, "NAK");
620                 return DHCP_EVENT_NO_LEASE;
621         }
622
623         if (r != DHCP_ACK)
624                 return -ENOMSG;
625
626         lease->next_server = ack->siaddr;
627
628         lease->address = ack->yiaddr;
629
630         if (lease->address == INADDR_ANY ||
631             lease->server_address == INADDR_ANY ||
632             lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
633                 return -ENOMSG;
634
635         r = DHCP_EVENT_IP_ACQUIRE;
636         if (client->lease) {
637                 if (client->lease->address != lease->address ||
638                     client->lease->subnet_mask != lease->subnet_mask ||
639                     client->lease->router != lease->router) {
640                         r = DHCP_EVENT_IP_CHANGE;
641                 }
642
643                 client->lease = sd_dhcp_lease_unref(client->lease);
644         }
645
646         client->lease = lease;
647         lease = NULL;
648
649         log_dhcp_client(client, "ACK");
650
651         return r;
652 }
653
654 static uint64_t client_compute_timeout(uint64_t request_sent,
655                                        uint32_t lifetime) {
656         return request_sent + (lifetime - 3) * USEC_PER_SEC +
657                 + (random_u32() & 0x1fffff);
658 }
659
660 static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
661         uint64_t next_timeout;
662         int r;
663
664         assert(client);
665         assert(client->event);
666
667         if (client->lease->lifetime < 10)
668                 return -EINVAL;
669
670         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
671         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
672         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
673
674         if (!client->lease->t1)
675                 client->lease->t1 = client->lease->lifetime / 2;
676
677         next_timeout = client_compute_timeout(client->request_sent,
678                                               client->lease->t1);
679         if (next_timeout < usec)
680                 return -EINVAL;
681
682         r = sd_event_add_monotonic(client->event,
683                                      &client->timeout_t1,
684                                      next_timeout,
685                                      10 * USEC_PER_MSEC,
686                                      client_timeout_t1, client);
687         if (r < 0)
688                 return r;
689
690         r = sd_event_source_set_priority(client->timeout_t1,
691                                          client->event_priority);
692         if (r < 0)
693                 return r;
694
695         if (!client->lease->t2)
696                 client->lease->t2 = client->lease->lifetime * 7 / 8;
697
698         if (client->lease->t2 < client->lease->t1)
699                 return -EINVAL;
700
701         if (client->lease->lifetime < client->lease->t2)
702                 return -EINVAL;
703
704         next_timeout = client_compute_timeout(client->request_sent,
705                                               client->lease->t2);
706         if (next_timeout < usec)
707                 return -EINVAL;
708
709         r = sd_event_add_monotonic(client->event,
710                                      &client->timeout_t2,
711                                      next_timeout,
712                                      10 * USEC_PER_MSEC,
713                                      client_timeout_t2, client);
714         if (r < 0)
715                 return r;
716
717         r = sd_event_source_set_priority(client->timeout_t2,
718                                          client->event_priority);
719         if (r < 0)
720                 return r;
721
722         next_timeout = client_compute_timeout(client->request_sent,
723                                               client->lease->lifetime);
724         if (next_timeout < usec)
725                 return -EINVAL;
726
727         r = sd_event_add_monotonic(client->event,
728                                      &client->timeout_expire, next_timeout,
729                                      10 * USEC_PER_MSEC,
730                                      client_timeout_expire, client);
731         if (r < 0)
732                 return r;
733
734         r = sd_event_source_set_priority(client->timeout_expire,
735                                          client->event_priority);
736         if (r < 0)
737                 return r;
738
739         return 0;
740 }
741
742 static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message,
743                                  int len, usec_t time_now) {
744         int r = 0, notify_event = 0;
745
746         assert(client);
747         assert(client->event);
748         assert(message);
749
750         if (len < DHCP_MESSAGE_SIZE) {
751                 log_dhcp_client(client, "message too small (%d bytes): "
752                                 "ignoring", len);
753                 return 0;
754         }
755
756         if (message->op != BOOTREPLY) {
757                 log_dhcp_client(client, "not a BOOTREPLY message: ignoring");
758                 return 0;
759         }
760
761         if (be32toh(message->xid) != client->xid) {
762                 log_dhcp_client(client, "received xid (%u) does not match "
763                                 "expected (%u): ignoring",
764                                 be32toh(message->xid), client->xid);
765                 return 0;
766         }
767
768         if (memcmp(&message->chaddr[0], &client->mac_addr.ether_addr_octet,
769                    ETHER_ADDR_LEN)) {
770                 log_dhcp_client(client, "received chaddr does not match "
771                                 "expected: ignoring");
772                 return 0;
773         }
774
775         switch (client->state) {
776         case DHCP_STATE_SELECTING:
777
778                 r = client_handle_offer(client, message, len);
779                 if (r >= 0) {
780
781                         client->timeout_resend =
782                                 sd_event_source_unref(client->timeout_resend);
783
784                         client->state = DHCP_STATE_REQUESTING;
785                         client->attempt = 1;
786
787                         r = sd_event_add_monotonic(client->event,
788                                                    &client->timeout_resend,
789                                                    time_now, 0,
790                                                    client_timeout_resend,
791                                                    client);
792                         if (r < 0)
793                                 goto error;
794
795                         r = sd_event_source_set_priority(client->timeout_resend,
796                                                          client->event_priority);
797                         if (r < 0)
798                                 goto error;
799                 }
800
801                 break;
802
803         case DHCP_STATE_REQUESTING:
804         case DHCP_STATE_RENEWING:
805         case DHCP_STATE_REBINDING:
806
807                 r = client_handle_ack(client, message, len);
808
809                 if (r == DHCP_EVENT_NO_LEASE)
810                         goto error;
811
812                 if (r >= 0) {
813                         client->timeout_resend =
814                                 sd_event_source_unref(client->timeout_resend);
815
816                         if (client->state == DHCP_STATE_REQUESTING)
817                                 notify_event = DHCP_EVENT_IP_ACQUIRE;
818                         else if (r != DHCP_EVENT_IP_ACQUIRE)
819                                 notify_event = r;
820
821                         client->state = DHCP_STATE_BOUND;
822                         client->attempt = 1;
823
824                         client->last_addr = client->lease->address;
825
826                         r = client_set_lease_timeouts(client, time_now);
827                         if (r < 0)
828                                 goto error;
829
830                         if (notify_event)
831                                 client_notify(client, notify_event);
832
833                         client->receive_message =
834                                 sd_event_source_unref(client->receive_message);
835                         close(client->fd);
836                         client->fd = -1;
837                 }
838
839                 r = 0;
840
841                 break;
842
843         case DHCP_STATE_INIT:
844         case DHCP_STATE_INIT_REBOOT:
845         case DHCP_STATE_REBOOTING:
846         case DHCP_STATE_BOUND:
847
848                 break;
849         }
850
851 error:
852         if (r < 0 || r == DHCP_EVENT_NO_LEASE)
853                 return client_stop(client, r);
854
855         return 0;
856 }
857
858 static int client_receive_message_udp(sd_event_source *s, int fd,
859                                       uint32_t revents, void *userdata) {
860         sd_dhcp_client *client = userdata;
861         _cleanup_free_ DHCPMessage *message = NULL;
862         int buflen = 0, len, r;
863         usec_t time_now;
864
865         assert(s);
866         assert(client);
867         assert(client->event);
868
869         r = ioctl(fd, FIONREAD, &buflen);
870         if (r < 0 || buflen <= 0)
871                 buflen = sizeof(DHCPMessage) + DHCP_MIN_OPTIONS_SIZE;
872
873         message = malloc0(buflen);
874         if (!message)
875                 return -ENOMEM;
876
877         len = read(fd, message, buflen);
878         if (len < 0)
879                 return 0;
880
881         r = sd_event_get_now_monotonic(client->event, &time_now);
882         if (r < 0)
883                 return client_stop(client, r);
884
885         return client_handle_message(client, message, len,
886                                      time_now);
887 }
888
889 static int client_receive_message_raw(sd_event_source *s, int fd,
890                                       uint32_t revents, void *userdata) {
891         sd_dhcp_client *client = userdata;
892         _cleanup_free_ DHCPPacket *packet = NULL;
893         usec_t time_now;
894         uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
895         struct iovec iov = {};
896         struct msghdr msg = {
897                 .msg_iov = &iov,
898                 .msg_iovlen = 1,
899                 .msg_control = cmsgbuf,
900                 .msg_controllen = sizeof(cmsgbuf),
901         };
902         struct cmsghdr *cmsg;
903         bool checksum = true;
904         int buflen = 0, len, r;
905
906         assert(s);
907         assert(client);
908         assert(client->event);
909
910         r = ioctl(fd, FIONREAD, &buflen);
911         if (r < 0 || buflen <= 0)
912                 buflen = sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE;
913
914         packet = malloc0(buflen);
915         if (!packet)
916                 return -ENOMEM;
917
918         iov.iov_base = packet;
919         iov.iov_len = buflen;
920
921         len = recvmsg(fd, &msg, 0);
922         if (len < 0) {
923                 log_dhcp_client(client, "could not receive message from raw "
924                                 "socket: %s", strerror(errno));
925                 return 0;
926         }
927
928         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
929                 if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) {
930                         struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
931
932                         checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
933                         break;
934                 }
935         }
936
937         r = dhcp_packet_verify_headers(packet, len, checksum);
938         if (r < 0)
939                 return 0;
940
941         len -= DHCP_IP_UDP_SIZE;
942
943         r = sd_event_get_now_monotonic(client->event, &time_now);
944         if (r < 0)
945                 return client_stop(client, r);
946
947         return client_handle_message(client, &packet->dhcp, len, time_now);
948 }
949
950 int sd_dhcp_client_start(sd_dhcp_client *client) {
951         int r;
952
953         assert_return(client, -EINVAL);
954         assert_return(client->event, -EINVAL);
955         assert_return(client->index > 0, -EINVAL);
956         assert_return(client->state == DHCP_STATE_INIT ||
957                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
958
959         client->xid = random_u32();
960
961         r = dhcp_network_bind_raw_socket(client->index, &client->link);
962
963         if (r < 0) {
964                 client_stop(client, r);
965                 return r;
966         }
967
968         client->fd = r;
969         client->start_time = now(CLOCK_MONOTONIC);
970         client->secs = 0;
971
972         log_dhcp_client(client, "STARTED");
973
974         return client_initialize_events(client, client_receive_message_raw,
975                                         client->start_time);
976 }
977
978 int sd_dhcp_client_stop(sd_dhcp_client *client) {
979         return client_stop(client, DHCP_EVENT_STOP);
980 }
981
982 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event,
983                                 int priority) {
984         int r;
985
986         assert_return(client, -EINVAL);
987         assert_return(!client->event, -EBUSY);
988
989         if (event)
990                 client->event = sd_event_ref(event);
991         else {
992                 r = sd_event_default(&client->event);
993                 if (r < 0)
994                         return 0;
995         }
996
997         client->event_priority = priority;
998
999         return 0;
1000 }
1001
1002 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1003         assert_return(client, -EINVAL);
1004
1005         client->event = sd_event_unref(client->event);
1006
1007         return 0;
1008 }
1009
1010 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1011         if (!client)
1012                 return NULL;
1013
1014         return client->event;
1015 }
1016
1017 void sd_dhcp_client_free(sd_dhcp_client *client) {
1018         if (!client)
1019                 return;
1020
1021         sd_dhcp_client_stop(client);
1022         sd_dhcp_client_detach_event(client);
1023
1024         free(client->req_opts);
1025         free(client);
1026 }
1027
1028 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
1029 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
1030
1031 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1032         _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1033
1034         assert_return(ret, -EINVAL);
1035
1036         client = new0(sd_dhcp_client, 1);
1037         if (!client)
1038                 return -ENOMEM;
1039
1040         client->state = DHCP_STATE_INIT;
1041         client->index = -1;
1042         client->fd = -1;
1043         client->attempt = 1;
1044
1045         client->req_opts_size = ELEMENTSOF(default_req_opts);
1046
1047         client->req_opts = memdup(default_req_opts, client->req_opts_size);
1048         if (!client->req_opts)
1049                 return -ENOMEM;
1050
1051         *ret = client;
1052         client = NULL;
1053
1054         return 0;
1055 }