chiark / gitweb /
4980174e005b665cbf157055f65d81882a738326
[elogind.git] / src / libsystemd-dhcp / 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
26 #include "util.h"
27 #include "list.h"
28
29 #include "dhcp-protocol.h"
30 #include "dhcp-internal.h"
31 #include "sd-dhcp-client.h"
32
33 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
34
35 struct DHCPLease {
36         uint32_t lifetime;
37         uint32_t address;
38         uint32_t server_address;
39         uint32_t subnet_mask;
40         uint32_t router;
41 };
42
43 typedef struct DHCPLease DHCPLease;
44
45 struct sd_dhcp_client {
46         DHCPState state;
47         sd_event *event;
48         sd_event_source *timeout_resend;
49         int index;
50         int fd;
51         union sockaddr_union link;
52         sd_event_source *receive_message;
53         uint8_t *req_opts;
54         size_t req_opts_size;
55         uint32_t last_addr;
56         struct ether_addr mac_addr;
57         uint32_t xid;
58         usec_t start_time;
59         unsigned int attempt;
60         DHCPLease *lease;
61 };
62
63 static const uint8_t default_req_opts[] = {
64         DHCP_OPTION_SUBNET_MASK,
65         DHCP_OPTION_ROUTER,
66         DHCP_OPTION_HOST_NAME,
67         DHCP_OPTION_DOMAIN_NAME,
68         DHCP_OPTION_DOMAIN_NAME_SERVER,
69         DHCP_OPTION_NTP_SERVER,
70 };
71
72 int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option)
73 {
74         size_t i;
75
76         assert_return(client, -EINVAL);
77         assert_return (client->state == DHCP_STATE_INIT, -EBUSY);
78
79         switch(option) {
80         case DHCP_OPTION_PAD:
81         case DHCP_OPTION_OVERLOAD:
82         case DHCP_OPTION_MESSAGE_TYPE:
83         case DHCP_OPTION_PARAMETER_REQUEST_LIST:
84         case DHCP_OPTION_END:
85                 return -EINVAL;
86
87         default:
88                 break;
89         }
90
91         for (i = 0; i < client->req_opts_size; i++)
92                 if (client->req_opts[i] == option)
93                         return -EEXIST;
94
95         if (!GREEDY_REALLOC(client->req_opts, client->req_opts_size,
96                             client->req_opts_size + 1))
97                 return -ENOMEM;
98
99         client->req_opts[client->req_opts_size - 1] = option;
100
101         return 0;
102 }
103
104 int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
105                                        const struct in_addr *last_addr)
106 {
107         assert_return(client, -EINVAL);
108         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
109
110         if (last_addr)
111                 client->last_addr = last_addr->s_addr;
112         else
113                 client->last_addr = INADDR_ANY;
114
115         return 0;
116 }
117
118 int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index)
119 {
120         assert_return(client, -EINVAL);
121         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
122         assert_return(interface_index >= -1, -EINVAL);
123
124         client->index = interface_index;
125
126         return 0;
127 }
128
129 int sd_dhcp_client_set_mac(sd_dhcp_client *client,
130                            const struct ether_addr *addr)
131 {
132         assert_return(client, -EINVAL);
133         assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
134
135         memcpy(&client->mac_addr, addr, ETH_ALEN);
136
137         return 0;
138 }
139
140 static int client_stop(sd_dhcp_client *client, int error)
141 {
142         assert_return(client, -EINVAL);
143         assert_return(client->state != DHCP_STATE_INIT &&
144                       client->state != DHCP_STATE_INIT_REBOOT, -EALREADY);
145
146         client->receive_message =
147                 sd_event_source_unref(client->receive_message);
148
149         if (client->fd >= 0)
150                 close(client->fd);
151         client->fd = -1;
152
153         client->timeout_resend = sd_event_source_unref(client->timeout_resend);
154
155         client->attempt = 1;
156
157         switch (client->state) {
158
159         case DHCP_STATE_INIT:
160         case DHCP_STATE_SELECTING:
161
162                 client->start_time = 0;
163                 client->state = DHCP_STATE_INIT;
164                 break;
165
166         case DHCP_STATE_INIT_REBOOT:
167         case DHCP_STATE_REBOOTING:
168         case DHCP_STATE_REQUESTING:
169         case DHCP_STATE_BOUND:
170         case DHCP_STATE_RENEWING:
171         case DHCP_STATE_REBINDING:
172
173                 break;
174         }
175
176         if (client->lease) {
177                 free(client->lease);
178                 client->lease = NULL;
179         }
180
181         return 0;
182 }
183
184 static int client_packet_init(sd_dhcp_client *client, uint8_t type,
185                               DHCPMessage *message, uint16_t secs,
186                               uint8_t **opt, size_t *optlen)
187 {
188         int err;
189
190         *opt = (uint8_t *)(message + 1);
191
192         if (*optlen < 4)
193                 return -ENOBUFS;
194         *optlen -= 4;
195
196         message->op = BOOTREQUEST;
197         message->htype = 1;
198         message->hlen = ETHER_ADDR_LEN;
199         message->xid = htobe32(client->xid);
200
201         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
202            refuse to issue an DHCP lease if 'secs' is set to zero */
203         message->secs = htobe16(secs);
204
205         memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
206         (*opt)[0] = 0x63;
207         (*opt)[1] = 0x82;
208         (*opt)[2] = 0x53;
209         (*opt)[3] = 0x63;
210
211         *opt += 4;
212
213         err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
214                                  &type);
215         if (err < 0)
216                 return err;
217
218         /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
219            Identifier option is not set */
220         err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
221                                  ETH_ALEN, &client->mac_addr);
222         if (err < 0)
223                 return err;
224
225         if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
226                 err = dhcp_option_append(opt, optlen,
227                                          DHCP_OPTION_PARAMETER_REQUEST_LIST,
228                                          client->req_opts_size,
229                                          client->req_opts);
230                 if (err < 0)
231                         return err;
232         }
233
234         return 0;
235 }
236
237 static uint16_t client_checksum(void *buf, int len)
238 {
239         uint32_t sum;
240         uint16_t *check;
241         int i;
242         uint8_t *odd;
243
244         sum = 0;
245         check = buf;
246
247         for (i = 0; i < len / 2 ; i++)
248                 sum += check[i];
249
250         if (len & 0x01) {
251                 odd = buf;
252                 sum += odd[len];
253         }
254
255         return ~((sum & 0xffff) + (sum >> 16));
256 }
257
258 static void client_append_ip_headers(DHCPPacket *packet, uint16_t len)
259 {
260         packet->ip.version = IPVERSION;
261         packet->ip.ihl = DHCP_IP_SIZE / 4;
262         packet->ip.tot_len = htobe16(len);
263
264         packet->ip.protocol = IPPROTO_UDP;
265         packet->ip.saddr = INADDR_ANY;
266         packet->ip.daddr = INADDR_BROADCAST;
267
268         packet->udp.source = htobe16(DHCP_PORT_CLIENT);
269         packet->udp.dest = htobe16(DHCP_PORT_SERVER);
270         packet->udp.len = htobe16(len - DHCP_IP_SIZE);
271
272         packet->ip.check = packet->udp.len;
273         packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
274
275         packet->ip.ttl = IPDEFTTL;
276         packet->ip.check = 0;
277         packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
278 }
279
280 static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
281 {
282         int err = 0;
283         _cleanup_free_ DHCPPacket *discover;
284         size_t optlen, len;
285         uint8_t *opt;
286
287         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
288         len = sizeof(DHCPPacket) + optlen;
289
290         discover = malloc0(len);
291
292         if (!discover)
293                 return -ENOMEM;
294
295         err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
296                                  secs, &opt, &optlen);
297         if (err < 0)
298                 return err;
299
300         if (client->last_addr != INADDR_ANY) {
301                 err = dhcp_option_append(&opt, &optlen,
302                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
303                                          4, &client->last_addr);
304                 if (err < 0)
305                         return err;
306         }
307
308         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
309         if (err < 0)
310                 return err;
311
312         client_append_ip_headers(discover, len);
313
314         err = dhcp_network_send_raw_socket(client->fd, &client->link,
315                                            discover, len);
316
317         return err;
318 }
319
320 static int client_send_request(sd_dhcp_client *client, uint16_t secs)
321 {
322         _cleanup_free_ DHCPPacket *request;
323         size_t optlen, len;
324         int err;
325         uint8_t *opt;
326
327         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
328         len = DHCP_MESSAGE_SIZE + optlen;
329
330         request = malloc0(len);
331         if (!request)
332                 return -ENOMEM;
333
334         err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
335                                  &opt, &optlen);
336         if (err < 0)
337                 return err;
338
339         if (client->state == DHCP_STATE_REQUESTING) {
340                 err = dhcp_option_append(&opt, &optlen,
341                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
342                                          4, &client->lease->address);
343                 if (err < 0)
344                         return err;
345
346                 err = dhcp_option_append(&opt, &optlen,
347                                          DHCP_OPTION_SERVER_IDENTIFIER,
348                                          4, &client->lease->server_address);
349                 if (err < 0)
350                         return err;
351         }
352
353         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
354         if (err < 0)
355                 return err;
356
357         client_append_ip_headers(request, len);
358
359         err = dhcp_network_send_raw_socket(client->fd, &client->link,
360                                            request, len);
361
362         return err;
363 }
364
365 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
366                                  void *userdata)
367 {
368         sd_dhcp_client *client = userdata;
369         usec_t next_timeout;
370         uint16_t secs;
371         int err = 0;
372
373         secs = (usec - client->start_time) / USEC_PER_SEC;
374
375         if (client->attempt < 64)
376                 client->attempt *= 2;
377
378         next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
379                 (random_u() & 0x1fffff);
380
381         err = sd_event_add_monotonic(client->event, next_timeout,
382                                      10 * USEC_PER_MSEC,
383                                      client_timeout_resend, client,
384                                      &client->timeout_resend);
385         if (err < 0)
386                 goto error;
387
388         switch (client->state) {
389         case DHCP_STATE_INIT:
390                 err = client_send_discover(client, secs);
391                 if (err >= 0) {
392                         client->state = DHCP_STATE_SELECTING;
393                         client->attempt = 1;
394                 } else {
395                         if (client->attempt >= 64)
396                                 goto error;
397                 }
398
399                 break;
400
401         case DHCP_STATE_SELECTING:
402                 err = client_send_discover(client, secs);
403                 if (err < 0 && client->attempt >= 64)
404                         goto error;
405
406                 break;
407
408         case DHCP_STATE_REQUESTING:
409                 err = client_send_request(client, secs);
410                 if (err < 0 && client->attempt >= 64)
411                          goto error;
412
413                 break;
414
415         case DHCP_STATE_INIT_REBOOT:
416         case DHCP_STATE_REBOOTING:
417         case DHCP_STATE_BOUND:
418         case DHCP_STATE_RENEWING:
419         case DHCP_STATE_REBINDING:
420
421                 break;
422         }
423
424         return 0;
425
426 error:
427         client_stop(client, err);
428
429         /* Errors were dealt with when stopping the client, don't spill
430            errors into the event loop handler */
431         return 0;
432 }
433
434 static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
435                               void *user_data)
436 {
437         DHCPLease *lease = user_data;
438         be32_t val;
439
440         switch(code) {
441
442         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
443                 if (len == 4) {
444                         memcpy(&val, option, 4);
445                         lease->lifetime = be32toh(val);
446                 }
447
448                 break;
449
450         case DHCP_OPTION_SERVER_IDENTIFIER:
451                 if (len >= 4)
452                         memcpy(&lease->server_address, option, 4);
453
454                 break;
455
456         case DHCP_OPTION_SUBNET_MASK:
457                 if (len >= 4)
458                         memcpy(&lease->subnet_mask, option, 4);
459
460                 break;
461
462         case DHCP_OPTION_ROUTER:
463                 if (len >= 4)
464                         memcpy(&lease->router, option, 4);
465
466                 break;
467         }
468
469         return 0;
470 }
471
472 static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
473                                 size_t len)
474 {
475         size_t hdrlen;
476         DHCPLease *lease;
477
478         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
479                 return -EINVAL;
480
481         hdrlen = offer->ip.ihl * 4;
482         if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
483                                                            hdrlen))
484                 return -EINVAL;
485
486         offer->ip.check = offer->udp.len;
487         offer->ip.ttl = 0;
488
489         if (hdrlen + be16toh(offer->udp.len) > len ||
490             client_checksum(&offer->ip.ttl, be16toh(offer->udp.len) + 12))
491                 return -EINVAL;
492
493         if (be16toh(offer->udp.source) != DHCP_PORT_SERVER ||
494             be16toh(offer->udp.dest) != DHCP_PORT_CLIENT)
495                 return -EINVAL;
496
497         if (offer->dhcp.op != BOOTREPLY)
498                 return -EINVAL;
499
500         if (be32toh(offer->dhcp.xid) != client->xid)
501                 return -EINVAL;
502
503         if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
504                     ETHER_ADDR_LEN))
505                 return -EINVAL;
506
507         lease = new0(DHCPLease, 1);
508         if (!lease)
509                 return -ENOMEM;
510
511         len = len - DHCP_IP_UDP_SIZE;
512         if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
513                               lease) != DHCP_OFFER)
514                 goto error;
515
516         lease->address = offer->dhcp.yiaddr;
517
518         if (lease->address == INADDR_ANY ||
519             lease->server_address == INADDR_ANY ||
520             lease->subnet_mask == INADDR_ANY ||
521             lease->lifetime == 0)
522                 goto error;
523
524         client->lease = lease;
525
526         return 0;
527
528 error:
529         free(lease);
530
531         return -ENOMSG;
532 }
533
534 static int client_receive_raw_message(sd_event_source *s, int fd,
535                                       uint32_t revents, void *userdata)
536 {
537         sd_dhcp_client *client = userdata;
538         uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
539         int buflen = sizeof(buf);
540         int len, err = 0;
541         DHCPPacket *message;
542         usec_t time_now;
543
544         len = read(fd, &buf, buflen);
545         if (len < 0)
546                 return 0;
547
548         err = sd_event_get_now_monotonic(client->event, &time_now);
549         if (err < 0)
550                 goto error;
551
552         message = (DHCPPacket *)&buf;
553
554         switch (client->state) {
555         case DHCP_STATE_SELECTING:
556
557                 if (client_receive_offer(client, message, len) >= 0) {
558
559                         client->timeout_resend =
560                                 sd_event_source_unref(client->timeout_resend);
561
562                         client->state = DHCP_STATE_REQUESTING;
563                         client->attempt = 1;
564
565                         err = sd_event_add_monotonic(client->event, time_now, 0,
566                                                      client_timeout_resend,
567                                                      client,
568                                                      &client->timeout_resend);
569                         if (err < 0)
570                                 goto error;
571                 }
572
573                 break;
574
575         case DHCP_STATE_INIT:
576         case DHCP_STATE_INIT_REBOOT:
577         case DHCP_STATE_REBOOTING:
578         case DHCP_STATE_REQUESTING:
579         case DHCP_STATE_BOUND:
580         case DHCP_STATE_RENEWING:
581         case DHCP_STATE_REBINDING:
582
583                 break;
584         }
585
586 error:
587         if (err < 0)
588                 return client_stop(client, err);
589
590         return 0;
591 }
592
593 int sd_dhcp_client_start(sd_dhcp_client *client)
594 {
595         int err;
596
597         assert_return(client, -EINVAL);
598         assert_return(client->index >= 0, -EINVAL);
599         assert_return(client->state == DHCP_STATE_INIT ||
600                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
601
602         client->xid = random_u();
603
604         client->fd = dhcp_network_bind_raw_socket(client->index,
605                                                   &client->link);
606
607         if (client->fd < 0) {
608                 err = client->fd;
609                 goto error;
610         }
611
612         err = sd_event_add_io(client->event, client->fd, EPOLLIN,
613                               client_receive_raw_message, client,
614                               &client->receive_message);
615         if (err < 0)
616                 goto error;
617
618         client->start_time = now(CLOCK_MONOTONIC);
619         err = sd_event_add_monotonic(client->event, client->start_time, 0,
620                                      client_timeout_resend, client,
621                                      &client->timeout_resend);
622         if (err < 0)
623                 goto error;
624
625         return 0;
626
627 error:
628         client_stop(client, err);
629
630         return err;
631 }
632
633 int sd_dhcp_client_stop(sd_dhcp_client *client)
634 {
635         return client_stop(client, 0);
636 }
637
638 sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
639 {
640         sd_dhcp_client *client;
641
642         assert_return(event, NULL);
643
644         client = new0(sd_dhcp_client, 1);
645         if (!client)
646                 return NULL;
647
648         client->event = sd_event_ref(event);
649         client->state = DHCP_STATE_INIT;
650         client->index = -1;
651         client->fd = -1;
652         client->attempt = 1;
653
654         client->req_opts_size = ELEMENTSOF(default_req_opts);
655
656         client->req_opts = memdup(default_req_opts, client->req_opts_size);
657         if (!client->req_opts) {
658                 free(client);
659                 return NULL;
660         }
661
662         return client;
663 }