chiark / gitweb /
dhcp: Add maximum message size option
[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         be16_t max_size;
190
191         *opt = (uint8_t *)(message + 1);
192
193         if (*optlen < 4)
194                 return -ENOBUFS;
195         *optlen -= 4;
196
197         message->op = BOOTREQUEST;
198         message->htype = 1;
199         message->hlen = ETHER_ADDR_LEN;
200         message->xid = htobe32(client->xid);
201
202         /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
203            refuse to issue an DHCP lease if 'secs' is set to zero */
204         message->secs = htobe16(secs);
205
206         memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
207         (*opt)[0] = 0x63;
208         (*opt)[1] = 0x82;
209         (*opt)[2] = 0x53;
210         (*opt)[3] = 0x63;
211
212         *opt += 4;
213
214         err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
215                                  &type);
216         if (err < 0)
217                 return err;
218
219         /* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
220            Identifier option is not set */
221         err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
222                                  ETH_ALEN, &client->mac_addr);
223         if (err < 0)
224                 return err;
225
226         if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
227                 err = dhcp_option_append(opt, optlen,
228                                          DHCP_OPTION_PARAMETER_REQUEST_LIST,
229                                          client->req_opts_size,
230                                          client->req_opts);
231                 if (err < 0)
232                         return err;
233
234                 /* Some DHCP servers will send bigger DHCP packets than the
235                    defined default size unless the Maximum Messge Size option
236                    is explicitely set */
237                 max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
238                                    DHCP_CLIENT_MIN_OPTIONS_SIZE);
239                 err = dhcp_option_append(opt, optlen,
240                                          DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
241                                          2, &max_size);
242                 if (err < 0)
243                         return err;
244         }
245
246         return 0;
247 }
248
249 static uint16_t client_checksum(void *buf, int len)
250 {
251         uint32_t sum;
252         uint16_t *check;
253         int i;
254         uint8_t *odd;
255
256         sum = 0;
257         check = buf;
258
259         for (i = 0; i < len / 2 ; i++)
260                 sum += check[i];
261
262         if (len & 0x01) {
263                 odd = buf;
264                 sum += odd[len];
265         }
266
267         return ~((sum & 0xffff) + (sum >> 16));
268 }
269
270 static void client_append_ip_headers(DHCPPacket *packet, uint16_t len)
271 {
272         packet->ip.version = IPVERSION;
273         packet->ip.ihl = DHCP_IP_SIZE / 4;
274         packet->ip.tot_len = htobe16(len);
275
276         packet->ip.protocol = IPPROTO_UDP;
277         packet->ip.saddr = INADDR_ANY;
278         packet->ip.daddr = INADDR_BROADCAST;
279
280         packet->udp.source = htobe16(DHCP_PORT_CLIENT);
281         packet->udp.dest = htobe16(DHCP_PORT_SERVER);
282         packet->udp.len = htobe16(len - DHCP_IP_SIZE);
283
284         packet->ip.check = packet->udp.len;
285         packet->udp.check = client_checksum(&packet->ip.ttl, len - 8);
286
287         packet->ip.ttl = IPDEFTTL;
288         packet->ip.check = 0;
289         packet->ip.check = client_checksum(&packet->ip, DHCP_IP_SIZE);
290 }
291
292 static int client_send_discover(sd_dhcp_client *client, uint16_t secs)
293 {
294         int err = 0;
295         _cleanup_free_ DHCPPacket *discover;
296         size_t optlen, len;
297         uint8_t *opt;
298
299         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
300         len = sizeof(DHCPPacket) + optlen;
301
302         discover = malloc0(len);
303
304         if (!discover)
305                 return -ENOMEM;
306
307         err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
308                                  secs, &opt, &optlen);
309         if (err < 0)
310                 return err;
311
312         if (client->last_addr != INADDR_ANY) {
313                 err = dhcp_option_append(&opt, &optlen,
314                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
315                                          4, &client->last_addr);
316                 if (err < 0)
317                         return err;
318         }
319
320         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
321         if (err < 0)
322                 return err;
323
324         client_append_ip_headers(discover, len);
325
326         err = dhcp_network_send_raw_socket(client->fd, &client->link,
327                                            discover, len);
328
329         return err;
330 }
331
332 static int client_send_request(sd_dhcp_client *client, uint16_t secs)
333 {
334         _cleanup_free_ DHCPPacket *request;
335         size_t optlen, len;
336         int err;
337         uint8_t *opt;
338
339         optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
340         len = DHCP_MESSAGE_SIZE + optlen;
341
342         request = malloc0(len);
343         if (!request)
344                 return -ENOMEM;
345
346         err = client_packet_init(client, DHCP_REQUEST, &request->dhcp, secs,
347                                  &opt, &optlen);
348         if (err < 0)
349                 return err;
350
351         if (client->state == DHCP_STATE_REQUESTING) {
352                 err = dhcp_option_append(&opt, &optlen,
353                                          DHCP_OPTION_REQUESTED_IP_ADDRESS,
354                                          4, &client->lease->address);
355                 if (err < 0)
356                         return err;
357
358                 err = dhcp_option_append(&opt, &optlen,
359                                          DHCP_OPTION_SERVER_IDENTIFIER,
360                                          4, &client->lease->server_address);
361                 if (err < 0)
362                         return err;
363         }
364
365         err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
366         if (err < 0)
367                 return err;
368
369         client_append_ip_headers(request, len);
370
371         err = dhcp_network_send_raw_socket(client->fd, &client->link,
372                                            request, len);
373
374         return err;
375 }
376
377 static int client_timeout_resend(sd_event_source *s, uint64_t usec,
378                                  void *userdata)
379 {
380         sd_dhcp_client *client = userdata;
381         usec_t next_timeout;
382         uint16_t secs;
383         int err = 0;
384
385         secs = (usec - client->start_time) / USEC_PER_SEC;
386
387         if (client->attempt < 64)
388                 client->attempt *= 2;
389
390         next_timeout = usec + (client->attempt - 1) * USEC_PER_SEC +
391                 (random_u() & 0x1fffff);
392
393         err = sd_event_add_monotonic(client->event, next_timeout,
394                                      10 * USEC_PER_MSEC,
395                                      client_timeout_resend, client,
396                                      &client->timeout_resend);
397         if (err < 0)
398                 goto error;
399
400         switch (client->state) {
401         case DHCP_STATE_INIT:
402                 err = client_send_discover(client, secs);
403                 if (err >= 0) {
404                         client->state = DHCP_STATE_SELECTING;
405                         client->attempt = 1;
406                 } else {
407                         if (client->attempt >= 64)
408                                 goto error;
409                 }
410
411                 break;
412
413         case DHCP_STATE_SELECTING:
414                 err = client_send_discover(client, secs);
415                 if (err < 0 && client->attempt >= 64)
416                         goto error;
417
418                 break;
419
420         case DHCP_STATE_REQUESTING:
421                 err = client_send_request(client, secs);
422                 if (err < 0 && client->attempt >= 64)
423                          goto error;
424
425                 break;
426
427         case DHCP_STATE_INIT_REBOOT:
428         case DHCP_STATE_REBOOTING:
429         case DHCP_STATE_BOUND:
430         case DHCP_STATE_RENEWING:
431         case DHCP_STATE_REBINDING:
432
433                 break;
434         }
435
436         return 0;
437
438 error:
439         client_stop(client, err);
440
441         /* Errors were dealt with when stopping the client, don't spill
442            errors into the event loop handler */
443         return 0;
444 }
445
446 static int client_parse_offer(uint8_t code, uint8_t len, const uint8_t *option,
447                               void *user_data)
448 {
449         DHCPLease *lease = user_data;
450         be32_t val;
451
452         switch(code) {
453
454         case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
455                 if (len == 4) {
456                         memcpy(&val, option, 4);
457                         lease->lifetime = be32toh(val);
458                 }
459
460                 break;
461
462         case DHCP_OPTION_SERVER_IDENTIFIER:
463                 if (len >= 4)
464                         memcpy(&lease->server_address, option, 4);
465
466                 break;
467
468         case DHCP_OPTION_SUBNET_MASK:
469                 if (len >= 4)
470                         memcpy(&lease->subnet_mask, option, 4);
471
472                 break;
473
474         case DHCP_OPTION_ROUTER:
475                 if (len >= 4)
476                         memcpy(&lease->router, option, 4);
477
478                 break;
479         }
480
481         return 0;
482 }
483
484 static int client_receive_offer(sd_dhcp_client *client, DHCPPacket *offer,
485                                 size_t len)
486 {
487         size_t hdrlen;
488         DHCPLease *lease;
489
490         if (len < (DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE))
491                 return -EINVAL;
492
493         hdrlen = offer->ip.ihl * 4;
494         if (hdrlen < 20 || hdrlen > len || client_checksum(&offer->ip,
495                                                            hdrlen))
496                 return -EINVAL;
497
498         offer->ip.check = offer->udp.len;
499         offer->ip.ttl = 0;
500
501         if (hdrlen + be16toh(offer->udp.len) > len ||
502             client_checksum(&offer->ip.ttl, be16toh(offer->udp.len) + 12))
503                 return -EINVAL;
504
505         if (be16toh(offer->udp.source) != DHCP_PORT_SERVER ||
506             be16toh(offer->udp.dest) != DHCP_PORT_CLIENT)
507                 return -EINVAL;
508
509         if (offer->dhcp.op != BOOTREPLY)
510                 return -EINVAL;
511
512         if (be32toh(offer->dhcp.xid) != client->xid)
513                 return -EINVAL;
514
515         if (memcmp(&offer->dhcp.chaddr[0], &client->mac_addr.ether_addr_octet,
516                     ETHER_ADDR_LEN))
517                 return -EINVAL;
518
519         lease = new0(DHCPLease, 1);
520         if (!lease)
521                 return -ENOMEM;
522
523         len = len - DHCP_IP_UDP_SIZE;
524         if (dhcp_option_parse(&offer->dhcp, len, client_parse_offer,
525                               lease) != DHCP_OFFER)
526                 goto error;
527
528         lease->address = offer->dhcp.yiaddr;
529
530         if (lease->address == INADDR_ANY ||
531             lease->server_address == INADDR_ANY ||
532             lease->subnet_mask == INADDR_ANY ||
533             lease->lifetime == 0)
534                 goto error;
535
536         client->lease = lease;
537
538         return 0;
539
540 error:
541         free(lease);
542
543         return -ENOMSG;
544 }
545
546 static int client_receive_raw_message(sd_event_source *s, int fd,
547                                       uint32_t revents, void *userdata)
548 {
549         sd_dhcp_client *client = userdata;
550         uint8_t buf[sizeof(DHCPPacket) + DHCP_CLIENT_MIN_OPTIONS_SIZE];
551         int buflen = sizeof(buf);
552         int len, err = 0;
553         DHCPPacket *message;
554         usec_t time_now;
555
556         len = read(fd, &buf, buflen);
557         if (len < 0)
558                 return 0;
559
560         err = sd_event_get_now_monotonic(client->event, &time_now);
561         if (err < 0)
562                 goto error;
563
564         message = (DHCPPacket *)&buf;
565
566         switch (client->state) {
567         case DHCP_STATE_SELECTING:
568
569                 if (client_receive_offer(client, message, len) >= 0) {
570
571                         client->timeout_resend =
572                                 sd_event_source_unref(client->timeout_resend);
573
574                         client->state = DHCP_STATE_REQUESTING;
575                         client->attempt = 1;
576
577                         err = sd_event_add_monotonic(client->event, time_now, 0,
578                                                      client_timeout_resend,
579                                                      client,
580                                                      &client->timeout_resend);
581                         if (err < 0)
582                                 goto error;
583                 }
584
585                 break;
586
587         case DHCP_STATE_INIT:
588         case DHCP_STATE_INIT_REBOOT:
589         case DHCP_STATE_REBOOTING:
590         case DHCP_STATE_REQUESTING:
591         case DHCP_STATE_BOUND:
592         case DHCP_STATE_RENEWING:
593         case DHCP_STATE_REBINDING:
594
595                 break;
596         }
597
598 error:
599         if (err < 0)
600                 return client_stop(client, err);
601
602         return 0;
603 }
604
605 int sd_dhcp_client_start(sd_dhcp_client *client)
606 {
607         int err;
608
609         assert_return(client, -EINVAL);
610         assert_return(client->index >= 0, -EINVAL);
611         assert_return(client->state == DHCP_STATE_INIT ||
612                       client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
613
614         client->xid = random_u();
615
616         client->fd = dhcp_network_bind_raw_socket(client->index,
617                                                   &client->link);
618
619         if (client->fd < 0) {
620                 err = client->fd;
621                 goto error;
622         }
623
624         err = sd_event_add_io(client->event, client->fd, EPOLLIN,
625                               client_receive_raw_message, client,
626                               &client->receive_message);
627         if (err < 0)
628                 goto error;
629
630         client->start_time = now(CLOCK_MONOTONIC);
631         err = sd_event_add_monotonic(client->event, client->start_time, 0,
632                                      client_timeout_resend, client,
633                                      &client->timeout_resend);
634         if (err < 0)
635                 goto error;
636
637         return 0;
638
639 error:
640         client_stop(client, err);
641
642         return err;
643 }
644
645 int sd_dhcp_client_stop(sd_dhcp_client *client)
646 {
647         return client_stop(client, 0);
648 }
649
650 sd_dhcp_client *sd_dhcp_client_new(sd_event *event)
651 {
652         sd_dhcp_client *client;
653
654         assert_return(event, NULL);
655
656         client = new0(sd_dhcp_client, 1);
657         if (!client)
658                 return NULL;
659
660         client->event = sd_event_ref(event);
661         client->state = DHCP_STATE_INIT;
662         client->index = -1;
663         client->fd = -1;
664         client->attempt = 1;
665
666         client->req_opts_size = ELEMENTSOF(default_req_opts);
667
668         client->req_opts = memdup(default_req_opts, client->req_opts_size);
669         if (!client->req_opts) {
670                 free(client);
671                 return NULL;
672         }
673
674         return client;
675 }