source: mainline/uspace/srv/net/dhcp/dhcp.c@ ee20e8a

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

More useful log messages.

  • Property mode set to 100644
File size: 14.7 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
420static void dhcp_link_set_failed(dhcp_link_t *dlink)
421{
422 log_msg(LOG_DEFAULT, LVL_NOTE, "Giving up on link %s",
423 dlink->link_info.name);
424 dlink->state = ds_fail;
425}
426
427int dhcpsrv_link_add(service_id_t link_id)
428{
429 dhcp_link_t *dlink;
430 int rc;
431
432 log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
433
434 if (dhcpsrv_link_find(link_id) != NULL) {
435 log_msg(LOG_DEFAULT, LVL_DEBUG, "Link %zu already added",
436 link_id);
437 return EEXIST;
438 }
439
440 dlink = calloc(1, sizeof(dhcp_link_t));
441 if (dlink == NULL)
442 return ENOMEM;
443
444 dlink->link_id = link_id;
445 dlink->timeout = fibril_timer_create();
446 if (dlink->timeout == NULL) {
447 rc = ENOMEM;
448 goto error;
449 }
450
451 /* Get link hardware address */
452 rc = inetcfg_link_get(link_id, &dlink->link_info);
453 if (rc != EOK) {
454 log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting properties "
455 "for link %zu.", link_id);
456 rc = EIO;
457 goto error;
458 }
459
460 rc = dhcp_transport_init(&dlink->dt, link_id, dhcpsrv_recv, dlink);
461 if (rc != EOK) {
462 log_msg(LOG_DEFAULT, LVL_ERROR, "Error initializing DHCP "
463 "transport for link %s.", dlink->link_info.name);
464 rc = EIO;
465 goto error;
466 }
467
468 dlink->state = ds_selecting;
469
470 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
471 rc = dhcp_send_discover(dlink);
472 if (rc != EOK) {
473 log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER.");
474 dhcp_link_set_failed(dlink);
475 rc = EIO;
476 goto error;
477 }
478
479 dlink->retries_left = dhcp_discover_retries;
480 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
481 dhcpsrv_discover_timeout, dlink);
482
483 list_append(&dlink->links, &dhcp_links);
484
485 return EOK;
486error:
487 if (dlink != NULL && dlink->timeout != NULL)
488 fibril_timer_destroy(dlink->timeout);
489 free(dlink);
490 return rc;
491}
492
493int dhcpsrv_link_remove(service_id_t link_id)
494{
495 return ENOTSUP;
496}
497
498static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
499{
500 int rc;
501
502 if (dlink->state != ds_selecting) {
503 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received offer in state "
504 " %d, ignoring.", (int)dlink->state);
505 return;
506 }
507
508 fibril_timer_clear(dlink->timeout);
509 dlink->offer = *offer;
510 dlink->state = ds_requesting;
511
512 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
513 rc = dhcp_send_request(dlink, offer);
514 if (rc != EOK) {
515 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
516 return;
517 }
518
519 dlink->retries_left = dhcp_request_retries;
520 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
521 dhcpsrv_request_timeout, dlink);
522}
523
524static void dhcpsrv_recv_ack(dhcp_link_t *dlink, dhcp_offer_t *offer)
525{
526 int rc;
527
528 if (dlink->state != ds_requesting) {
529 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received ack in state "
530 " %d, ignoring.", (int)dlink->state);
531 return;
532 }
533
534 fibril_timer_clear(dlink->timeout);
535 dlink->offer = *offer;
536 dlink->state = ds_bound;
537
538 rc = dhcp_cfg_create(dlink->link_id, offer);
539 if (rc != EOK) {
540 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
541 return;
542 }
543
544 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: Successfully configured.",
545 dlink->link_info.name);
546}
547
548static void dhcpsrv_recv(void *arg, void *msg, size_t size)
549{
550 dhcp_link_t *dlink = (dhcp_link_t *)arg;
551 dhcp_offer_t offer;
552 int rc;
553
554 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: dhcpsrv_recv() %zu bytes",
555 dlink->link_info.name, size);
556
557 rc = dhcp_parse_reply(msg, size, &offer);
558 if (rc != EOK) {
559 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error parsing reply");
560 return;
561 }
562
563 switch (offer.msg_type) {
564 case msg_dhcpoffer:
565 dhcpsrv_recv_offer(dlink, &offer);
566 break;
567 case msg_dhcpack:
568 dhcpsrv_recv_ack(dlink, &offer);
569 break;
570 default:
571 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received unexpected "
572 "message type. %d", (int)offer.msg_type);
573 break;
574 }
575}
576
577static void dhcpsrv_discover_timeout(void *arg)
578{
579 dhcp_link_t *dlink = (dhcp_link_t *)arg;
580 int rc;
581
582 assert(dlink->state == ds_selecting);
583 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_discover_timeout",
584 dlink->link_info.name);
585
586 if (dlink->retries_left == 0) {
587 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
588 dhcp_link_set_failed(dlink);
589 return;
590 }
591 --dlink->retries_left;
592
593 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
594 rc = dhcp_send_discover(dlink);
595 if (rc != EOK) {
596 log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER");
597 dhcp_link_set_failed(dlink);
598 return;
599 }
600
601 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
602 dhcpsrv_discover_timeout, dlink);
603}
604
605static void dhcpsrv_request_timeout(void *arg)
606{
607 dhcp_link_t *dlink = (dhcp_link_t *)arg;
608 int rc;
609
610 assert(dlink->state == ds_requesting);
611 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_request_timeout",
612 dlink->link_info.name);
613
614 if (dlink->retries_left == 0) {
615 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
616 dhcp_link_set_failed(dlink);
617 return;
618 }
619 --dlink->retries_left;
620
621 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
622 rc = dhcp_send_request(dlink, &dlink->offer);
623 if (rc != EOK) {
624 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
625 dhcp_link_set_failed(dlink);
626 return;
627 }
628
629 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
630 dhcpsrv_request_timeout, dlink);
631}
632
633/** @}
634 */
Note: See TracBrowser for help on using the repository browser.