chiark / gitweb /
sd-dhcp-client: refactor state machine check
[elogind.git] / src / libsystemd / 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-internal.h"
32 #include "sd-dhcp-client.h"
33
34 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
35
36 #define client_state_machine_check(s, r)                                \
37         do {                                                            \
38                 if (s != DHCP_STATE_BOUND &&                            \
39                     s != DHCP_STATE_RENEWING &&                         \
40                     s != DHCP_STATE_REBINDING) {                        \
41                         return (r);                                     \
42                 }                                                       \
43         } while (false)
44
45 struct DHCPLease {
46         uint32_t t1;
47         uint32_t t2;
48         uint32_t lifetime;
49         be32_t address;
50         be32_t server_address;
51         be32_t subnet_mask;
52         be32_t router;
53         struct in_addr *dns;
54         size_t dns_size;
55         uint16_t mtu;
56         char *domainname;
57         char *hostname;
58 };
59
60 typedef struct DHCPLease DHCPLease;
61
62 struct sd_dhcp_client {
63         DHCPState state;
64         sd_event *event;
65         int event_priority;
66         sd_event_source *timeout_resend;
67         int index;
68         int fd;
69         union sockaddr_union link;
70         sd_event_source *receive_message;
71         uint8_t *req_opts;
72         size_t req_opts_allocated;
73         size_t req_opts_size;
74         be32_t last_addr;
75         struct ether_addr mac_addr;
76         uint32_t xid;
77         usec_t start_time;
78         unsigned int attempt;
79         usec_t request_sent;
80         sd_event_source *timeout_t1;
81         sd_event_source *timeout_t2;
82         sd_event_source *timeout_expire;
83         sd_dhcp_client_cb_t cb;
84         void *userdata;
85         DHCPLease *lease;
86 };
87
88 static const uint8_t default_req_opts[] = {
89         DHCP_OPTION_SUBNET_MASK,
90         DHCP_OPTION_ROUTER,
91         DHCP_OPTION_HOST_NAME,
92         DHCP_OPTION_DOMAIN_NAME,
93         DHCP_OPTION_DOMAIN_NAME_SERVER,
94         DHCP_OPTION_NTP_SERVER,
95 };
96
97 static int client_receive_message(sd_event_source *s, int fd,
98                                   uint32_t revents, void *userdata);
99
100 int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb,
101                                 void *userdata) {
102         assert_return(client, -EINVAL);
103
104         client->cb = cb;
105         client->userdata = userdata;
106
107         return 0;
108 }
109
110 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
111         size_t i;
112
113         assert_return(client, -EINVAL);
114         assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
115
116         switch(option) {
117         case DHCP_OPTION_PAD:
118         case DHCP_OPTION_OVERLOAD:
119         case DHCP_OPTION_MESSAGE_TYPE:
120         case DHCP_OPTION_PARAMETER_REQUEST_LIST:
121         case DHCP_OPTION_END:
122                 return -EINVAL;
123
124         default:
125                 break;
126         }
127
128         for (i = 0; i < client->req_opts_size; i++)
129                 if (client->req_opts[i] == option)
130                         return -EEXIST;
131
132         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_allocated,
133                             client->req_opts_size + 1))
134                 return -ENOMEM;
135
136         client->req_opts[client->req_opts_size++] = option;
137
138         return 0;
139 }
140
141 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
142                                        const struct in_addr *last_addr) {
143         assert_return(client, -EINVAL);
144         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
145
146         if (last_addr)
147                 client->last_addr = last_addr->s_addr;
148         else
149                 client->last_addr = INADDR_ANY;
150
151         return 0;
152 }
153
154 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) {
155         assert_return(client, -EINVAL);
156         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
157         assert_return(interface_index >= -1, -EINVAL);
158
159         client->index = interface_index;
160
161         return 0;
162 }
163
164 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
165                            const struct ether_addr *addr) {
166         assert_return(client, -EINVAL);
167         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
168
169         memcpy(&client->mac_addr, addr, ETH_ALEN);
170
171         return 0;
172 }
173
174 int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr) {
175         assert_return(client, -EINVAL);
176         assert_return(addr, -EINVAL);
177
178         client_state_machine_check (client->state, -EADDRNOTAVAIL);
179
180         addr->s_addr = client->lease->address;
181
182         return 0;
183 }
184
185 int sd_dhcp_client_get_mtu(sd_dhcp_client *client, uint16_t *mtu) {
186         assert_return(client, -EINVAL);
187         assert_return(mtu, -EINVAL);
188
189         client_state_machine_check (client->state, -EADDRNOTAVAIL);
190
191         if (client->lease->mtu)
192                 *mtu = client->lease->mtu;
193         else
194                 return -ENOENT;
195
196         return 0;
197 }
198
199 int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr **addr, size_t *addr_size) {
200         assert_return(client, -EINVAL);
201         assert_return(addr, -EINVAL);
202         assert_return(addr_size, -EINVAL);
203
204         client_state_machine_check (client->state, -EADDRNOTAVAIL);
205
206         if (client->lease->dns_size) {
207                 *addr_size = client->lease->dns_size;
208                 *addr = client->lease->dns;
209         } else
210                 return -ENOENT;
211
212         return 0;
213 }
214
215 int sd_dhcp_client_get_domainname(sd_dhcp_client *client, const char **domainname) {
216         assert_return(client, -EINVAL);
217         assert_return(domainname, -EINVAL);
218
219         client_state_machine_check (client->state, -EADDRNOTAVAIL);
220
221         if (client->lease->domainname)
222                 *domainname = client->lease->domainname;
223         else
224                 return -ENOENT;
225
226         return 0;
227 }
228
229 int sd_dhcp_client_get_hostname(sd_dhcp_client *client, const char **hostname) {
230         assert_return(client, -EINVAL);
231         assert_return(hostname, -EINVAL);
232
233         client_state_machine_check (client->state, -EADDRNOTAVAIL);
234
235         if (client->lease->hostname)
236                 *hostname = client->lease->hostname;
237         else
238                 return -ENOENT;
239
240         return 0;
241 }
242
243 int sd_dhcp_client_prefixlen(const struct in_addr *addr) {
244         int len = 0;
245         uint32_t mask;
246
247         assert_return(addr, -EADDRNOTAVAIL);
248
249         mask = be32toh(addr->s_addr);
250         while (mask) {
251                 len++;
252                 mask = mask << 1;
253         }
254
255         return len;
256 }
257
258 int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr) {
259         assert_return(client, -EINVAL);
260         assert_return(addr, -EINVAL);
261
262         client_state_machine_check (client->state, -EADDRNOTAVAIL);
263
264         addr->s_addr = client->lease->router;
265
266         return 0;
267 }
268
269 int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr) {
270         assert_return(client, -EINVAL);
271         assert_return(addr, -EINVAL);
272
273         client_state_machine_check (client->state, -EADDRNOTAVAIL);
274
275         addr->s_addr = client->lease->subnet_mask;
276
277         return 0;
278 }
279
280 static int client_notify(sd_dhcp_client *client, int event) {
281         if (client->cb)
282                 client->cb(client, event, client->userdata);
283
284         return 0;
285 }
286
287 static void lease_free(DHCPLease *lease) {
288         if (!lease)
289                 return;
290
291         free(lease->hostname);
292         free(lease->domainname);
293         free(lease->dns);
294         free(lease);
295 }
296
297 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, lease_free);
298 #define _cleanup_lease_free_ _cleanup_(lease_freep)
299
300 static int client_stop(sd_dhcp_client *client, int error) {
301         assert_return(client, -EINVAL);
302
303         client->receive_message =
304                 sd_event_source_unref(client->receive_message);
305
306         if (client->fd >= 0)
307                 close(client->fd);
308         client->fd = -1;
309
310         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
311
312         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
313         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
314         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
315
316         client->attempt = 1;
317
318         client_notify(client, error);
319
320         switch (client->state) {
321
322         case DHCP_STATE_INIT:
323         case DHCP_STATE_SELECTING:
324         case DHCP_STATE_REQUESTING:
325         case DHCP_STATE_BOUND:
326
327                 client->start_time = 0;
328                 client->state = DHCP_STATE_INIT;
329                 break;
330
331         case DHCP_STATE_INIT_REBOOT:
332         case DHCP_STATE_REBOOTING:
333         case DHCP_STATE_RENEWING:
334         case DHCP_STATE_REBINDING:
335
336                 break;
337         }
338
339         if (client->lease) {
340                 lease_free(client->lease);
341                 client->lease = NULL;
342         }
343
344         return 0;
345 }
346
347 static int client_packet_init(sd_dhcp_client *client, uint8_t type,
348                               DHCPMessage *message, uint16_t secs,
349                               uint8_t **opt, size_t *optlen) {
350         int err;
351         be16_t max_size;
352
353         *opt = (uint8_t *)(message + 1);
354
355         if (*optlen < 4)
356                 return -ENOBUFS;
357         *optlen -= 4;
358
359         message->op = BOOTREQUEST;
360         message->htype = 1;
361         message->hlen = ETHER_ADDR_LEN;
362         message->xid = htobe32(client->xid);
363
364         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
365            refuse to issue an DHCP lease if 'secs' is set to zero */
366         message->secs = htobe16(secs);
367
368         if (client->state == DHCP_STATE_RENEWING ||
369             client->state == DHCP_STATE_REBINDING)
370                 message->ciaddr = client->lease->address;
371
372         memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
373         (*opt)[0] = 0x63;
374         (*opt)[1] = 0x82;
375         (*opt)[2] = 0x53;
376         (*opt)[3] = 0x63;
377
378         *opt += 4;
379
380         err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
381                                  &type);
382         if (err < 0)
383                 return err;
384
385         /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
386            Identifier option is not set */
387         err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
388                                  ETH_ALEN, &client->mac_addr);
389         if (err < 0)
390                 return err;
391
392         if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
393                 err = dhcp_option_append(opt, optlen,
394                                          DHCP_OPTION_PARAMETER_REQUEST_LIST,
395                                          client->req_opts_size,
396                                          client->req_opts);
397                 if (err < 0)
398                         return err;
399
400                 /* Some DHCP servers will send bigger DHCP packets than the
401                    defined default size unless the Maximum Messge Size option
402                    is explicitely set */
403                 max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
404                                    DHCP_CLIENT_MIN_OPTIONS_SIZE);
405                 err = dhcp_option_append(opt, optlen,
406                                          DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
407                                          2, &max_size);
408                 if (err < 0)
409                         return err;
410         }
411
412         return 0;
413 }
414
415 static uint16_t client_checksum(void *buf, int len) {
416         uint32_t sum;
417         uint16_t *check;
418         int i;
419         uint8_t *odd;
420
421         sum = 0;
422         check = buf;
423
424         for (i = 0; i < len / 2 ; i++)
425                 sum += check[i];
426
427         if (len & 0x01) {
428                 odd = buf;
429                 sum += odd[len - 1];
430         }
431
432         while (sum >> 16)
433                 sum = (sum & 0xffff) + (sum >> 16);
434
435         return ~sum;
436 }
437
438 static void client_append_ip_headers(DHCPPacket *packet, uint16_t len) {
439         packet->ip.version = IPVERSION;
440         packet->ip.ihl = DHCP_IP_SIZE / 4;
441         packet->ip.tot_len = htobe16(len);
442
443         packet->ip.protocol = IPPROTO_UDP;
444         packet->ip.saddr = INADDR_ANY;
445         packet->ip.daddr = INADDR_BROADCAST;
446
447         packet->udp.source = htobe16(DHCP_PORT_CLIENT);
448         packet->udp.dest = htobe16(DHCP_PORT_SERVER);
449         packet->udp.len = htobe16(len - DHCP_IP_SIZE);
450
451         packet->ip.check = packet->udp.len;
452         packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
453
454         packet->ip.ttl = IPDEFTTL;
455         packet->ip.check = 0;
456         packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
457 }
458
459 static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
460         int err = 0;
461         _cleanup_free_ DHCPPacket *discover;
462         size_t optlen, len;
463         uint8_t *opt;
464
465         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
466         len = sizeof(DHCPPacket) + optlen;
467
468         discover = malloc0(len);
469
470         if (!discover)
471                 return -ENOMEM;
472
473         err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
474                                  secs, &opt, &optlen);
475         if (err < 0)
476                 return err;
477
478         if (client->last_addr != INADDR_ANY) {
479                 err = dhcp_option_append(&opt, &optlen,
480                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
481                                          4, &client->last_addr);
482                 if (err < 0)
483                         return err;
484         }
485
486         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
487         if (err < 0)
488                 return err;
489
490         client_append_ip_headers(discover, len);
491
492         err = dhcp_network_send_raw_socket(client->fd, &client->link,
493                                            discover, len);
494
495         return err;
496 }
497
498 static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
499         _cleanup_free_ DHCPPacket *request;
500         size_t optlen, len;
501         int err;
502         uint8_t *opt;
503
504         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
505         len = DHCP_MESSAGE_SIZE + optlen;
506
507         request = malloc0(len);
508         if (!request)
509                 return -ENOMEM;
510
511         err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
512                                  &opt, &optlen);
513         if (err < 0)
514                 return err;
515
516         if (client->state == DHCP_STATE_REQUESTING) {
517                 err = dhcp_option_append(&opt, &optlen,
518                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
519                                          4, &client->lease->address);
520                 if (err < 0)
521                         return err;
522
523                 err = dhcp_option_append(&opt, &optlen,
524                                          DHCP_OPTION_SERVER_IDENTIFIER,
525                                          4, &client->lease->server_address);
526                 if (err < 0)
527                         return err;
528         }
529
530         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
531         if (err < 0)
532                 return err;
533
534         if (client->state == DHCP_STATE_RENEWING) {
535                 err = dhcp_network_send_udp_socket(client->fd,
536                                                    client->lease->server_address,
537                                                    &request->dhcp,
538                                                    len - DHCP_IP_UDP_SIZE);
539         } else {
540                 client_append_ip_headers(request, len);
541
542                 err = dhcp_network_send_raw_socket(client->fd, &client->link,
543                                                    request, len);
544         }
545
546         return err;
547 }
548
549 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
550                                  void *userdata) {
551         sd_dhcp_client *client = userdata;
552         usec_t next_timeout = 0;
553         uint32_t time_left;
554         uint16_t secs;
555         int r = 0;
556
557         assert(s);
558         assert(client);
559         assert(client->event);
560
561         switch (client->state) {
562         case DHCP_STATE_RENEWING:
563
564                 time_left = (client->lease->t2 - client->lease->t1)/2;
565                 if (time_left < 60)
566                         time_left = 60;
567
568                 next_timeout = usec + time_left * USEC_PER_SEC;
569
570                 break;
571
572         case DHCP_STATE_REBINDING:
573
574                 time_left = (client->lease->lifetime - client->lease->t2)/2;
575                 if (time_left < 60)
576                         time_left = 60;
577
578                 next_timeout = usec + time_left * USEC_PER_SEC;
579                 break;
580
581         case DHCP_STATE_INIT:
582         case DHCP_STATE_INIT_REBOOT:
583         case DHCP_STATE_REBOOTING:
584         case DHCP_STATE_SELECTING:
585         case DHCP_STATE_REQUESTING:
586         case DHCP_STATE_BOUND:
587
588                 if (client->attempt < 64)
589                         client->attempt *= 2;
590
591                 next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC;
592
593                 break;
594         }
595
596         next_timeout += (random_u32() & 0x1fffff);
597
598         r = sd_event_add_monotonic(client->event, next_timeout,
599                                      10 * USEC_PER_MSEC,
600                                      client_timeout_resend, client,
601                                      &client->timeout_resend);
602         if (r < 0)
603                 goto error;
604
605         r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
606         if (r < 0)
607                 goto error;
608
609         secs = (usec - client->start_time) / USEC_PER_SEC;
610
611         switch (client->state) {
612         case DHCP_STATE_INIT:
613                 r = client_send_discover(client, secs);
614                 if (r >= 0) {
615                         client->state = DHCP_STATE_SELECTING;
616                         client->attempt = 1;
617                 } else {
618                         if (client->attempt >= 64)
619                                 goto error;
620                 }
621
622                 break;
623
624         case DHCP_STATE_SELECTING:
625                 r = client_send_discover(client, secs);
626                 if (r < 0 && client->attempt >= 64)
627                         goto error;
628
629                 break;
630
631         case DHCP_STATE_REQUESTING:
632         case DHCP_STATE_RENEWING:
633         case DHCP_STATE_REBINDING:
634                 r = client_send_request(client, secs);
635                 if (r < 0 && client->attempt >= 64)
636                          goto error;
637
638                 client->request_sent = usec;
639
640                 break;
641
642         case DHCP_STATE_INIT_REBOOT:
643         case DHCP_STATE_REBOOTING:
644         case DHCP_STATE_BOUND:
645
646                 break;
647         }
648
649         return 0;
650
651 error:
652         client_stop(client, r);
653
654         /* Errors were dealt with when stopping the client, don't spill
655            errors into the event loop handler */
656         return 0;
657 }
658
659 static int client_initialize_events(sd_dhcp_client *client, usec_t usec) {
660         int r;
661
662         assert(client);
663         assert(client->event);
664
665         r = sd_event_add_io(client->event, client->fd, EPOLLIN,
666                             client_receive_message, client,
667                             &client->receive_message);
668         if (r < 0)
669                 goto error;
670
671         r = sd_event_source_set_priority(client->receive_message, client->event_priority);
672         if (r < 0)
673                 goto error;
674
675         r = sd_event_add_monotonic(client->event, usec, 0,
676                                    client_timeout_resend, client,
677                                    &client->timeout_resend);
678         if (r < 0)
679                 goto error;
680
681         r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
682
683 error:
684         if (r < 0)
685                 client_stop(client, r);
686
687         return 0;
688
689 }
690
691 static int client_timeout_expire(sd_event_source *s, uint64_t usec,
692                                  void *userdata) {
693         sd_dhcp_client *client = userdata;
694
695         client_stop(client, DHCP_EVENT_EXPIRED);
696
697         return 0;
698 }
699
700 static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
701         sd_dhcp_client *client = userdata;
702         int r;
703
704         if (client->fd >= 0) {
705                 client->receive_message =
706                         sd_event_source_unref(client->receive_message);
707                 close(client->fd);
708                 client->fd = -1;
709         }
710
711         client->state = DHCP_STATE_REBINDING;
712         client->attempt = 1;
713
714         r = dhcp_network_bind_raw_socket(client->index, &client->link);
715         if (r < 0) {
716                 client_stop(client, r);
717                 return 0;
718         }
719
720         client->fd = r;
721
722         return client_initialize_events(client, usec);
723 }
724
725 static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
726         sd_dhcp_client *client = userdata;
727         int r;
728
729         client->state = DHCP_STATE_RENEWING;
730         client->attempt = 1;
731
732         r = dhcp_network_bind_udp_socket(client->index,
733                                          client->lease->address);
734         if (r < 0) {
735                 client_stop(client, r);
736                 return 0;
737         }
738
739         client->fd = r;
740
741         return client_initialize_events(client, usec);
742 }
743
744 static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
745                               void *user_data) {
746         DHCPLease *lease = user_data;
747         be32_t val;
748
749         switch(code) {
750
751         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
752                 if (len == 4) {
753                         memcpy(&val, option, 4);
754                         lease->lifetime = be32toh(val);
755                 }
756
757                 break;
758
759         case DHCP_OPTION_SERVER_IDENTIFIER:
760                 if (len >= 4)
761                         memcpy(&lease->server_address, option, 4);
762
763                 break;
764
765         case DHCP_OPTION_SUBNET_MASK:
766                 if (len >= 4)
767                         memcpy(&lease->subnet_mask, option, 4);
768
769                 break;
770
771         case DHCP_OPTION_ROUTER:
772                 if (len >= 4)
773                         memcpy(&lease->router, option, 4);
774
775                 break;
776
777         case DHCP_OPTION_DOMAIN_NAME_SERVER:
778                 if (len >= 4) {
779                         unsigned i;
780
781                         lease->dns_size = len / 4;
782
783                         free(lease->dns);
784                         lease->dns = new0(struct in_addr, lease->dns_size);
785                         if (!lease->dns)
786                                 return -ENOMEM;
787
788                         for (i = 0; i < lease->dns_size; i++) {
789                                 memcpy(&lease->dns[i].s_addr, option + 4 * i, 4);
790                         }
791                 }
792
793                 break;
794
795         case DHCP_OPTION_INTERFACE_MTU:
796                 if (len >= 2) {
797                         be16_t mtu;
798
799                         memcpy(&mtu, option, 2);
800                         lease->mtu = be16toh(mtu);
801
802                         if (lease->mtu < 68)
803                                 lease->mtu = 0;
804                 }
805
806                 break;
807
808         case DHCP_OPTION_DOMAIN_NAME:
809                 if (len >= 1) {
810                         free(lease->domainname);
811                         lease->domainname = strndup((const char *)option, len);
812                 }
813
814                 break;
815
816         case DHCP_OPTION_HOST_NAME:
817                 if (len >= 1) {
818                         free(lease->hostname);
819                         lease->hostname = strndup((const char *)option, len);
820                 }
821
822                 break;
823
824         case DHCP_OPTION_RENEWAL_T1_TIME:
825                 if (len == 4) {
826                         memcpy(&val, option, 4);
827                         lease->t1 = be32toh(val);
828                 }
829
830                 break;
831
832         case DHCP_OPTION_REBINDING_T2_TIME:
833                 if (len == 4) {
834                         memcpy(&val, option, 4);
835                         lease->t2 = be32toh(val);
836                 }
837
838                 break;
839         }
840
841         return 0;
842 }
843
844 static int client_verify_headers(sd_dhcp_client *client, DHCPPacket *message,
845                                  size_t len) {
846         size_t hdrlen;
847
848         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
849                 return -EINVAL;
850
851         hdrlen = message->ip.ihl * 4;
852         if (hdrlen < 20 || hdrlen > len || client_checksum(&message->ip,
853                                                            hdrlen))
854                 return -EINVAL;
855
856         message->ip.check = message->udp.len;
857         message->ip.ttl = 0;
858
859         if (hdrlen + be16toh(message->udp.len) > len ||
860             client_checksum(&message->ip.ttl, be16toh(message->udp.len) + 12))
861                 return -EINVAL;
862
863         if (be16toh(message->udp.source) != DHCP_PORT_SERVER ||
864             be16toh(message->udp.dest) != DHCP_PORT_CLIENT)
865                 return -EINVAL;
866
867         if (message->dhcp.op != BOOTREPLY)
868                 return -EINVAL;
869
870         if (be32toh(message->dhcp.xid) != client->xid)
871                 return -EINVAL;
872
873         if (memcmp(&message->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
874                     ETHER_ADDR_LEN))
875                 return -EINVAL;
876
877         return 0;
878 }
879
880 static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
881                                 size_t len) {
882         _cleanup_lease_free_ DHCPLease *lease = NULL;
883         int r;
884
885         r = client_verify_headers(client, offer, len);
886         if (r < 0)
887                 return r;
888
889         lease = new0(DHCPLease, 1);
890         if (!lease)
891                 return -ENOMEM;
892
893         len = len - DHCP_IP_UDP_SIZE;
894         r = dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
895                               lease);
896         if (r != DHCP_OFFER)
897                 return -ENOMSG;
898
899         lease->address = offer->dhcp.yiaddr;
900
901         if (lease->address == INADDR_ANY ||
902             lease->server_address == INADDR_ANY ||
903             lease->subnet_mask == INADDR_ANY ||
904             lease->lifetime == 0)
905                 return -ENOMSG;
906
907         client->lease = lease;
908         lease = NULL;
909
910         return 0;
911 }
912
913 static int client_receive_ack(sd_dhcp_client *client, const uint8_t *buf,
914                               size_t len) {
915         DHCPPacket *ack;
916         DHCPMessage *dhcp;
917         _cleanup_lease_free_ DHCPLease *lease = NULL;
918         int r;
919
920         if (client->state == DHCP_STATE_RENEWING) {
921                 dhcp = (DHCPMessage *)buf;
922         } else {
923                 ack = (DHCPPacket *)buf;
924
925                 r = client_verify_headers(client, ack, len);
926                 if (r < 0)
927                         return r;
928
929                 dhcp = &ack->dhcp;
930                 len -= DHCP_IP_UDP_SIZE;
931         }
932
933         lease = new0(DHCPLease, 1);
934         if (!lease)
935                 return -ENOMEM;
936
937         r = dhcp_option_parse(dhcp, len, client_parse_offer, lease);
938         if (r == DHCP_NAK)
939                 return DHCP_EVENT_NO_LEASE;
940
941         if (r != DHCP_ACK)
942                 return -ENOMSG;
943
944         lease->address = dhcp->yiaddr;
945
946         if (lease->address == INADDR_ANY ||
947             lease->server_address == INADDR_ANY ||
948             lease->subnet_mask == INADDR_ANY || lease->lifetime == 0)
949                 return -ENOMSG;
950
951         r = DHCP_EVENT_IP_ACQUIRE;
952         if (client->lease) {
953                 if (client->lease->address != lease->address ||
954                     client->lease->subnet_mask != lease->subnet_mask ||
955                     client->lease->router != lease->router) {
956                         r = DHCP_EVENT_IP_CHANGE;
957                 }
958
959                 lease_free(client->lease);
960         }
961
962         client->lease = lease;
963         lease = NULL;
964
965         return r;
966 }
967
968 static uint64_t client_compute_timeout(uint64_t request_sent,
969                                        uint32_t lifetime) {
970         return request_sent + (lifetime - 3) * USEC_PER_SEC +
971                 + (random_u32() & 0x1fffff);
972 }
973
974 static int client_set_lease_timeouts(sd_dhcp_client *client, uint64_t usec) {
975         uint64_t next_timeout;
976         int r;
977
978         assert(client);
979         assert(client->event);
980
981         if (client->lease->lifetime < 10)
982                 return -EINVAL;
983
984         client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
985         client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
986         client->timeout_expire = sd_event_source_unref(client->timeout_expire);
987
988         if (!client->lease->t1)
989                 client->lease->t1 = client->lease->lifetime / 2;
990
991         next_timeout = client_compute_timeout(client->request_sent,
992                                               client->lease->t1);
993         if (next_timeout < usec)
994                 return -EINVAL;
995
996         r = sd_event_add_monotonic(client->event, next_timeout,
997                                      10 * USEC_PER_MSEC,
998                                      client_timeout_t1, client,
999                                      &client->timeout_t1);
1000         if (r < 0)
1001                 return r;
1002
1003         r = sd_event_source_set_priority(client->timeout_t1, client->event_priority);
1004         if (r < 0)
1005                 return r;
1006
1007         if (!client->lease->t2)
1008                 client->lease->t2 = client->lease->lifetime * 7 / 8;
1009
1010         if (client->lease->t2 < client->lease->t1)
1011                 return -EINVAL;
1012
1013         if (client->lease->lifetime < client->lease->t2)
1014                 return -EINVAL;
1015
1016         next_timeout = client_compute_timeout(client->request_sent,
1017                                               client->lease->t2);
1018         if (next_timeout < usec)
1019                 return -EINVAL;
1020
1021         r = sd_event_add_monotonic(client->event, next_timeout,
1022                                      10 * USEC_PER_MSEC,
1023                                      client_timeout_t2, client,
1024                                      &client->timeout_t2);
1025         if (r < 0)
1026                 return r;
1027
1028         r = sd_event_source_set_priority(client->timeout_t2, client->event_priority);
1029         if (r < 0)
1030                 return r;
1031
1032         next_timeout = client_compute_timeout(client->request_sent,
1033                                               client->lease->lifetime);
1034         if (next_timeout < usec)
1035                 return -EINVAL;
1036
1037         r = sd_event_add_monotonic(client->event, next_timeout,
1038                                      10 * USEC_PER_MSEC,
1039                                      client_timeout_expire, client,
1040                                      &client->timeout_expire);
1041         if (r < 0)
1042                 return r;
1043
1044         r = sd_event_source_set_priority(client->timeout_expire, client->event_priority);
1045         if (r < 0)
1046                 return r;
1047
1048         return 0;
1049 }
1050
1051 static int client_receive_message(sd_event_source *s, int fd,
1052                                   uint32_t revents, void *userdata) {
1053         sd_dhcp_client *client = userdata;
1054         uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
1055         int buflen = sizeof(buf);
1056         int len, r = 0, notify_event = 0;
1057         DHCPPacket *message;
1058         usec_t time_now;
1059
1060         assert(s);
1061         assert(client);
1062         assert(client->event);
1063
1064         len = read(fd, &buf, buflen);
1065         if (len < 0)
1066                 return 0;
1067
1068         r = sd_event_get_now_monotonic(client->event, &time_now);
1069         if (r < 0)
1070                 goto error;
1071
1072         switch (client->state) {
1073         case DHCP_STATE_SELECTING:
1074
1075                 message = (DHCPPacket *)&buf;
1076
1077                 if (client_receive_offer(client, message, len) >= 0) {
1078
1079                         client->timeout_resend =
1080                                 sd_event_source_unref(client->timeout_resend);
1081
1082                         client->state = DHCP_STATE_REQUESTING;
1083                         client->attempt = 1;
1084
1085                         r = sd_event_add_monotonic(client->event, time_now, 0,
1086                                                    client_timeout_resend,
1087                                                    client,
1088                                                    &client->timeout_resend);
1089                         if (r < 0)
1090                                 goto error;
1091
1092                         r = sd_event_source_set_priority(client->timeout_resend, client->event_priority);
1093                         if (r < 0)
1094                                 goto error;
1095                 }
1096
1097                 break;
1098
1099         case DHCP_STATE_REQUESTING:
1100         case DHCP_STATE_RENEWING:
1101         case DHCP_STATE_REBINDING:
1102
1103                 r = client_receive_ack(client, buf, len);
1104
1105                 if (r == DHCP_EVENT_NO_LEASE)
1106                         goto error;
1107
1108                 if (r >= 0) {
1109                         client->timeout_resend =
1110                                 sd_event_source_unref(client->timeout_resend);
1111
1112                         if (client->state == DHCP_STATE_REQUESTING)
1113                                 notify_event = DHCP_EVENT_IP_ACQUIRE;
1114                         else if (r != DHCP_EVENT_IP_ACQUIRE)
1115                                 notify_event = r;
1116
1117                         client->state = DHCP_STATE_BOUND;
1118                         client->attempt = 1;
1119
1120                         client->last_addr = client->lease->address;
1121
1122                         r = client_set_lease_timeouts(client, time_now);
1123                         if (r < 0)
1124                                 goto error;
1125
1126                         if (notify_event)
1127                                 client_notify(client, notify_event);
1128
1129                         client->receive_message =
1130                                 sd_event_source_unref(client->receive_message);
1131                         close(client->fd);
1132                         client->fd = -1;
1133                 }
1134
1135                 r = 0;
1136
1137                 break;
1138
1139         case DHCP_STATE_INIT:
1140         case DHCP_STATE_INIT_REBOOT:
1141         case DHCP_STATE_REBOOTING:
1142         case DHCP_STATE_BOUND:
1143
1144                 break;
1145         }
1146
1147 error:
1148         if (r < 0 || r == DHCP_EVENT_NO_LEASE)
1149                 return client_stop(client, r);
1150
1151         return 0;
1152 }
1153
1154 int sd_dhcp_client_start(sd_dhcp_client *client) {
1155         int r;
1156
1157         assert_return(client, -EINVAL);
1158         assert_return(client->event, -EINVAL);
1159         assert_return(client->index > 0, -EINVAL);
1160         assert_return(client->state == DHCP_STATE_INIT ||
1161                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
1162
1163         client->xid = random_u32();
1164
1165         r = dhcp_network_bind_raw_socket(client->index, &client->link);
1166
1167         if (r < 0) {
1168                 client_stop(client, r);
1169                 return r;
1170         }
1171
1172         client->fd = r;
1173         client->start_time = now(CLOCK_MONOTONIC);
1174
1175         return client_initialize_events(client, client->start_time);
1176 }
1177
1178 int sd_dhcp_client_stop(sd_dhcp_client *client) {
1179         return client_stop(client, DHCP_EVENT_STOP);
1180 }
1181
1182 int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority) {
1183         int r;
1184
1185         assert_return(client, -EINVAL);
1186         assert_return(!client->event, -EBUSY);
1187
1188         if (event)
1189                 client->event = sd_event_ref(event);
1190         else {
1191                 r = sd_event_default(&client->event);
1192                 if (r < 0)
1193                         return 0;
1194         }
1195
1196         client->event_priority = priority;
1197
1198         return 0;
1199 }
1200
1201 int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
1202         assert_return(client, -EINVAL);
1203
1204         client->event = sd_event_unref(client->event);
1205
1206         return 0;
1207 }
1208
1209 sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
1210         if (!client)
1211                 return NULL;
1212
1213         return client->event;
1214 }
1215
1216 void sd_dhcp_client_free(sd_dhcp_client *client) {
1217         if (!client)
1218                 return;
1219
1220         sd_dhcp_client_stop(client);
1221         sd_dhcp_client_detach_event(client);
1222
1223         free(client->req_opts);
1224         free(client);
1225 }
1226
1227 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_free);
1228 #define _cleanup_dhcp_client_free_ _cleanup_(sd_dhcp_client_freep)
1229
1230 int sd_dhcp_client_new(sd_dhcp_client **ret) {
1231         _cleanup_dhcp_client_free_ sd_dhcp_client *client = NULL;
1232
1233         assert_return(ret, -EINVAL);
1234
1235         client = new0(sd_dhcp_client, 1);
1236         if (!client)
1237                 return -ENOMEM;
1238
1239         client->state = DHCP_STATE_INIT;
1240         client->index = -1;
1241         client->fd = -1;
1242         client->attempt = 1;
1243
1244         client->req_opts_size = ELEMENTSOF(default_req_opts);
1245
1246         client->req_opts = memdup(default_req_opts, client->req_opts_size);
1247         if (!client->req_opts)
1248                 return -ENOMEM;
1249
1250         *ret = client;
1251         client = NULL;
1252
1253         return 0;
1254 }