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