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