source: mainline/uspace/srv/net/dhcp/dhcp.c@ 1b0602a3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1b0602a3 was 1b0602a3, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Limit the number of retries.

  • Property mode set to 100644
File size: 14.2 KB
Line 
1/*
2 * Copyright (c) 2013 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup dhcp
30 * @{
31 */
32/**
33 * @file
34 * @brief DHCP client
35 */
36
37#include <adt/list.h>
38#include <bitops.h>
39#include <fibril_synch.h>
40#include <inet/addr.h>
41#include <inet/dnsr.h>
42#include <inet/inetcfg.h>
43#include <io/log.h>
44#include <loc.h>
45#include <net/in.h>
46#include <net/inet.h>
47#include <net/socket.h>
48#include <stdio.h>
49#include <stdlib.h>
50
51#include "dhcp.h"
52#include "dhcp_std.h"
53#include "transport.h"
54
55enum {
56 /** In microseconds */
57 dhcp_discover_timeout_val = 5 * 1000 * 1000,
58 /** In microseconds */
59 dhcp_request_timeout_val = 1 * 1000 * 1000,
60 dhcp_discover_retries = 5,
61 dhcp_request_retries = 3
62};
63
64#define MAX_MSG_SIZE 1024
65static uint8_t msgbuf[MAX_MSG_SIZE];
66
67/** List of registered links (of dhcp_link_t) */
68static list_t dhcp_links;
69
70static void dhcpsrv_discover_timeout(void *);
71static void dhcpsrv_request_timeout(void *);
72
73typedef enum {
74 ds_bound,
75 ds_fail,
76 ds_init,
77 ds_init_reboot,
78 ds_rebinding,
79 ds_renewing,
80 ds_requesting,
81 ds_selecting
82} dhcp_state_t;
83
84typedef struct {
85 /** Message type */
86 enum dhcp_msg_type msg_type;
87 /** Offered address */
88 inet_naddr_t oaddr;
89 /** Server address */
90 inet_addr_t srv_addr;
91 /** Router address */
92 inet_addr_t router;
93 /** DNS server */
94 inet_addr_t dns_server;
95} dhcp_offer_t;
96
97typedef struct {
98 /** Link to dhcp_links list */
99 link_t links;
100 /** Link service ID */
101 service_id_t link_id;
102 /** Link info */
103 inet_link_info_t link_info;
104 /** Transport */
105 dhcp_transport_t dt;
106 /** Transport timeout */
107 fibril_timer_t *timeout;
108 /** Number of retries */
109 int retries_left;
110 /** Link state */
111 dhcp_state_t state;
112 /** Last received offer */
113 dhcp_offer_t offer;
114} dhcp_link_t;
115
116static void dhcpsrv_recv(void *, void *, size_t);
117
118/** Decode subnet mask into subnet prefix length. */
119static int subnet_mask_decode(uint32_t mask, int *bits)
120{
121 int zbits;
122 uint32_t nmask;
123
124 if (mask == 0xffffffff) {
125 *bits = 32;
126 return EOK;
127 }
128
129 zbits = 1 + fnzb32(mask ^ 0xffffffff);
130 nmask = BIT_RRANGE(uint32_t, zbits);
131
132 if ((mask ^ nmask) != 0xffffffff) {
133 /* The mask is not in the form 1**n,0**m */
134 return EINVAL;
135 }
136
137 *bits = 32 - zbits;
138 return EOK;
139}
140
141static uint32_t dhcp_uint32_decode(uint8_t *data)
142{
143 return
144 ((uint32_t)data[0] << 24) |
145 ((uint32_t)data[1] << 16) |
146 ((uint32_t)data[2] << 8) |
147 ((uint32_t)data[3]);
148}
149
150static int dhcp_send_discover(dhcp_link_t *dlink)
151{
152 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
153 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
154
155 memset(msgbuf, 0, MAX_MSG_SIZE);
156 hdr->op = op_bootrequest;
157 hdr->htype = 1; /* AHRD_ETHERNET */
158 hdr->hlen = sizeof(addr48_t);
159 hdr->xid = host2uint32_t_be(42);
160 hdr->flags = flag_broadcast;
161
162 addr48(dlink->link_info.mac_addr, hdr->chaddr);
163 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
164
165 opt[0] = opt_msg_type;
166 opt[1] = 1;
167 opt[2] = msg_dhcpdiscover;
168 opt[3] = opt_end;
169
170 return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + 4);
171}
172
173static int dhcp_send_request(dhcp_link_t *dlink, dhcp_offer_t *offer)
174{
175 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
176 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
177 size_t i;
178
179 memset(msgbuf, 0, MAX_MSG_SIZE);
180 hdr->op = op_bootrequest;
181 hdr->htype = 1; /* AHRD_ETHERNET */
182 hdr->hlen = 6;
183 hdr->xid = host2uint32_t_be(42);
184 hdr->flags = flag_broadcast;
185 hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
186 addr48(dlink->link_info.mac_addr, hdr->chaddr);
187 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
188
189 i = 0;
190
191 opt[i++] = opt_msg_type;
192 opt[i++] = 1;
193 opt[i++] = msg_dhcprequest;
194
195 opt[i++] = opt_req_ip_addr;
196 opt[i++] = 4;
197 opt[i++] = offer->oaddr.addr >> 24;
198 opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
199 opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
200 opt[i++] = offer->oaddr.addr & 0xff;
201
202 opt[i++] = opt_server_id;
203 opt[i++] = 4;
204 opt[i++] = offer->srv_addr.addr >> 24;
205 opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
206 opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
207 opt[i++] = offer->srv_addr.addr & 0xff;
208
209 opt[i++] = opt_end;
210
211 return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + i);
212}
213
214static int dhcp_parse_reply(void *msg, size_t size, dhcp_offer_t *offer)
215{
216 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
217 inet_addr_t yiaddr;
218 inet_addr_t siaddr;
219 inet_addr_t giaddr;
220 uint32_t subnet_mask;
221 bool have_subnet_mask = false;
222 bool have_server_id = false;
223 int subnet_bits;
224 char *saddr;
225 uint8_t opt_type, opt_len;
226 uint8_t *msgb;
227 int rc;
228 size_t i;
229
230 log_msg(LOG_DEFAULT, LVL_DEBUG, "Receive reply");
231 memset(offer, 0, sizeof(*offer));
232
233 yiaddr.family = AF_INET;
234 yiaddr.addr = uint32_t_be2host(hdr->yiaddr);
235 rc = inet_addr_format(&yiaddr, &saddr);
236 if (rc != EOK)
237 return rc;
238
239 log_msg(LOG_DEFAULT, LVL_DEBUG, "Your IP address: %s", saddr);
240 free(saddr);
241
242 siaddr.family = AF_INET;
243 siaddr.addr = uint32_t_be2host(hdr->siaddr);
244 rc = inet_addr_format(&siaddr, &saddr);
245 if (rc != EOK)
246 return rc;
247
248 log_msg(LOG_DEFAULT, LVL_DEBUG, "Next server IP address: %s", saddr);
249 free(saddr);
250
251 giaddr.family = AF_INET;
252 giaddr.addr = uint32_t_be2host(hdr->giaddr);
253 rc = inet_addr_format(&giaddr, &saddr);
254 if (rc != EOK)
255 return rc;
256
257 log_msg(LOG_DEFAULT, LVL_DEBUG, "Relay agent IP address: %s", saddr);
258 free(saddr);
259
260 offer->oaddr.family = AF_INET;
261 offer->oaddr.addr = yiaddr.addr;
262
263 msgb = (uint8_t *)msg;
264
265 i = sizeof(dhcp_hdr_t);
266 while (i < size) {
267 opt_type = msgb[i++];
268
269 if (opt_type == opt_pad)
270 continue;
271 if (opt_type == opt_end)
272 break;
273
274 if (i >= size)
275 return EINVAL;
276
277 opt_len = msgb[i++];
278
279 if (i + opt_len > size)
280 return EINVAL;
281
282 switch (opt_type) {
283 case opt_subnet_mask:
284 if (opt_len != 4)
285 return EINVAL;
286 subnet_mask = dhcp_uint32_decode(&msgb[i]);
287 rc = subnet_mask_decode(subnet_mask, &subnet_bits);
288 if (rc != EOK)
289 return EINVAL;
290 offer->oaddr.prefix = subnet_bits;
291 have_subnet_mask = true;
292 break;
293 case opt_msg_type:
294 if (opt_len != 1)
295 return EINVAL;
296 offer->msg_type = msgb[i];
297 break;
298 case opt_server_id:
299 if (opt_len != 4)
300 return EINVAL;
301 offer->srv_addr.family = AF_INET;
302 offer->srv_addr.addr = dhcp_uint32_decode(&msgb[i]);
303 have_server_id = true;
304 break;
305 case opt_router:
306 if (opt_len != 4)
307 return EINVAL;
308 offer->router.family = AF_INET;
309 offer->router.addr = dhcp_uint32_decode(&msgb[i]);
310 break;
311 case opt_dns_server:
312 if (opt_len != 4)
313 return EINVAL;
314 offer->dns_server.family = AF_INET;
315 offer->dns_server.addr = dhcp_uint32_decode(&msgb[i]);
316 break;
317 case opt_end:
318 break;
319 default:
320 break;
321 }
322
323 /* Advance to the next option */
324 i = i + opt_len;
325 }
326
327 if (!have_server_id) {
328 log_msg(LOG_DEFAULT, LVL_ERROR, "Missing server ID option.");
329 return rc;
330 }
331
332 if (!have_subnet_mask) {
333 log_msg(LOG_DEFAULT, LVL_ERROR, "Missing subnet mask option.");
334 return rc;
335 }
336
337 rc = inet_naddr_format(&offer->oaddr, &saddr);
338 if (rc != EOK)
339 return rc;
340
341 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offered network address: %s", saddr);
342 free(saddr);
343
344 if (offer->router.addr != 0) {
345 rc = inet_addr_format(&offer->router, &saddr);
346 if (rc != EOK)
347 return rc;
348
349 log_msg(LOG_DEFAULT, LVL_DEBUG, "Router address: %s", saddr);
350 free(saddr);
351 }
352
353 if (offer->dns_server.addr != 0) {
354 rc = inet_addr_format(&offer->dns_server, &saddr);
355 if (rc != EOK)
356 return rc;
357
358 log_msg(LOG_DEFAULT, LVL_DEBUG, "DNS server: %s", saddr);
359 free(saddr);
360 }
361
362 return EOK;
363}
364
365static int dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
366{
367 int rc;
368 service_id_t addr_id;
369 service_id_t sroute_id;
370 inet_naddr_t defr;
371
372 rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
373 &addr_id);
374 if (rc != EOK) {
375 log_msg(LOG_DEFAULT, LVL_ERROR,
376 "Error creating IP address %s (%d)", "dhcp4a", rc);
377 return rc;
378 }
379
380 if (offer->router.addr != 0) {
381 defr.family = AF_INET;
382 defr.addr = 0;
383 defr.prefix = 0;
384
385 rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
386 if (rc != EOK) {
387 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating "
388 "default route %s (%d).", "dhcpdef", rc);
389 return rc;
390 }
391 }
392
393 if (offer->dns_server.addr != 0) {
394 rc = dnsr_set_srvaddr(&offer->dns_server);
395 if (rc != EOK) {
396 log_msg(LOG_DEFAULT, LVL_ERROR, "Error setting "
397 "nameserver address (%d))", rc);
398 return rc;
399 }
400 }
401
402 return EOK;
403}
404
405void dhcpsrv_links_init(void)
406{
407 list_initialize(&dhcp_links);
408}
409
410static dhcp_link_t *dhcpsrv_link_find(service_id_t link_id)
411{
412 list_foreach(dhcp_links, links, dhcp_link_t, dlink) {
413 if (dlink->link_id == link_id)
414 return dlink;
415 }
416
417 return NULL;
418}
419
420int dhcpsrv_link_add(service_id_t link_id)
421{
422 dhcp_link_t *dlink;
423 int rc;
424
425 log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
426
427 if (dhcpsrv_link_find(link_id) != NULL) {
428 log_msg(LOG_DEFAULT, LVL_DEBUG, "Link %zu already added",
429 link_id);
430 return EEXIST;
431 }
432
433 dlink = calloc(1, sizeof(dhcp_link_t));
434 if (dlink == NULL)
435 return ENOMEM;
436
437 dlink->link_id = link_id;
438 dlink->timeout = fibril_timer_create();
439 if (dlink->timeout == NULL) {
440 rc = ENOMEM;
441 goto error;
442 }
443
444 /* Get link hardware address */
445 rc = inetcfg_link_get(link_id, &dlink->link_info);
446 if (rc != EOK) {
447 log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting properties "
448 "for link %zu.", link_id);
449 rc = EIO;
450 goto error;
451 }
452
453 rc = dhcp_transport_init(&dlink->dt, link_id, dhcpsrv_recv, dlink);
454 if (rc != EOK) {
455 log_msg(LOG_DEFAULT, LVL_ERROR, "Error initializing DHCP "
456 "transport for link %s.", dlink->link_info.name);
457 rc = EIO;
458 goto error;
459 }
460
461 dlink->state = ds_selecting;
462
463 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
464 rc = dhcp_send_discover(dlink);
465 if (rc != EOK) {
466 dlink->state = ds_fail;
467 rc = EIO;
468 goto error;
469 }
470
471 dlink->retries_left = dhcp_discover_retries;
472 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
473 dhcpsrv_discover_timeout, dlink);
474
475 list_append(&dlink->links, &dhcp_links);
476
477 return EOK;
478error:
479 if (dlink != NULL && dlink->timeout != NULL)
480 fibril_timer_destroy(dlink->timeout);
481 free(dlink);
482 return rc;
483}
484
485int dhcpsrv_link_remove(service_id_t link_id)
486{
487 return ENOTSUP;
488}
489
490static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
491{
492 int rc;
493
494 if (dlink->state != ds_selecting) {
495 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received offer in state "
496 " %d, ignoring.", (int)dlink->state);
497 return;
498 }
499
500 fibril_timer_clear(dlink->timeout);
501 dlink->offer = *offer;
502 dlink->state = ds_requesting;
503
504 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
505 rc = dhcp_send_request(dlink, offer);
506 if (rc != EOK) {
507 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
508 return;
509 }
510
511 dlink->retries_left = dhcp_request_retries;
512 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
513 dhcpsrv_request_timeout, dlink);
514}
515
516static void dhcpsrv_recv_ack(dhcp_link_t *dlink, dhcp_offer_t *offer)
517{
518 int rc;
519
520 if (dlink->state != ds_requesting) {
521 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received ack in state "
522 " %d, ignoring.", (int)dlink->state);
523 return;
524 }
525
526 fibril_timer_clear(dlink->timeout);
527 dlink->offer = *offer;
528 dlink->state = ds_bound;
529
530 rc = dhcp_cfg_create(dlink->link_id, offer);
531 if (rc != EOK) {
532 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
533 return;
534 }
535}
536
537static void dhcpsrv_recv(void *arg, void *msg, size_t size)
538{
539 dhcp_link_t *dlink = (dhcp_link_t *)arg;
540 dhcp_offer_t offer;
541 int rc;
542
543 log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_recv() %zu bytes", size);
544
545 rc = dhcp_parse_reply(msg, size, &offer);
546 if (rc != EOK) {
547 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error parsing reply");
548 return;
549 }
550
551 switch (offer.msg_type) {
552 case msg_dhcpoffer:
553 dhcpsrv_recv_offer(dlink, &offer);
554 break;
555 case msg_dhcpack:
556 dhcpsrv_recv_ack(dlink, &offer);
557 break;
558 default:
559 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received unexpected "
560 "message type. %d", (int)offer.msg_type);
561 break;
562 }
563}
564
565static void dhcpsrv_discover_timeout(void *arg)
566{
567 dhcp_link_t *dlink = (dhcp_link_t *)arg;
568 int rc;
569
570 assert(dlink->state == ds_selecting);
571 log_msg(LOG_DEFAULT, LVL_NOTE, "dcpsrv_discover_timeout");
572
573 if (dlink->retries_left == 0) {
574 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
575 dlink->state = ds_fail;
576 return;
577 }
578 --dlink->retries_left;
579
580 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
581 rc = dhcp_send_discover(dlink);
582 if (rc != EOK) {
583 log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER");
584 dlink->state = ds_fail;
585 return;
586 }
587
588 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
589 dhcpsrv_discover_timeout, dlink);
590}
591
592static void dhcpsrv_request_timeout(void *arg)
593{
594 dhcp_link_t *dlink = (dhcp_link_t *)arg;
595 int rc;
596
597 assert(dlink->state == ds_requesting);
598 log_msg(LOG_DEFAULT, LVL_NOTE, "dcpsrv_request_timeout");
599
600 if (dlink->retries_left == 0) {
601 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
602 dlink->state = ds_fail;
603 return;
604 }
605 --dlink->retries_left;
606
607 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
608 rc = dhcp_send_request(dlink, &dlink->offer);
609 if (rc != EOK) {
610 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
611 dlink->state = ds_fail;
612 return;
613 }
614
615 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
616 dhcpsrv_request_timeout, dlink);
617}
618
619/** @}
620 */
Note: See TracBrowser for help on using the repository browser.