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