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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 15.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 <byteorder.h>
40#include <errno.h>
41#include <str_error.h>
42#include <fibril_synch.h>
43#include <inet/addr.h>
44#include <inet/dnsr.h>
45#include <inet/inetcfg.h>
46#include <io/log.h>
47#include <loc.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <str.h>
51
52#include "dhcp.h"
53#include "dhcp_std.h"
54#include "transport.h"
55
56enum {
57 /** In microseconds */
58 dhcp_discover_timeout_val = 5 * 1000 * 1000,
59 /** In microseconds */
60 dhcp_request_timeout_val = 1 * 1000 * 1000,
61 dhcp_discover_retries = 5,
62 dhcp_request_retries = 3
63};
64
65#define MAX_MSG_SIZE 1024
66static uint8_t msgbuf[MAX_MSG_SIZE];
67
68/** List of registered links (of dhcp_link_t) */
69static list_t dhcp_links;
70
71static void dhcpsrv_discover_timeout(void *);
72static void dhcpsrv_request_timeout(void *);
73
74typedef enum {
75 ds_bound,
76 ds_fail,
77 ds_init,
78 ds_init_reboot,
79 ds_rebinding,
80 ds_renewing,
81 ds_requesting,
82 ds_selecting
83} dhcp_state_t;
84
85typedef struct {
86 /** Message type */
87 enum dhcp_msg_type msg_type;
88 /** Offered address */
89 inet_naddr_t oaddr;
90 /** Server address */
91 inet_addr_t srv_addr;
92 /** Router address */
93 inet_addr_t router;
94 /** DNS server */
95 inet_addr_t dns_server;
96} dhcp_offer_t;
97
98typedef struct {
99 /** Link to dhcp_links list */
100 link_t links;
101 /** Link service ID */
102 service_id_t link_id;
103 /** Link info */
104 inet_link_info_t link_info;
105 /** Transport */
106 dhcp_transport_t dt;
107 /** Transport timeout */
108 fibril_timer_t *timeout;
109 /** Number of retries */
110 int retries_left;
111 /** Link state */
112 dhcp_state_t state;
113 /** Last received offer */
114 dhcp_offer_t offer;
115} dhcp_link_t;
116
117static void dhcpsrv_recv(void *, void *, size_t);
118
119/** Decode subnet mask into subnet prefix length. */
120static errno_t subnet_mask_decode(uint32_t mask, int *bits)
121{
122 int zbits;
123 uint32_t nmask;
124
125 if (mask == 0xffffffff) {
126 *bits = 32;
127 return EOK;
128 }
129
130 zbits = 1 + fnzb32(mask ^ 0xffffffff);
131 nmask = BIT_RRANGE(uint32_t, zbits);
132
133 if ((mask ^ nmask) != 0xffffffff) {
134 /* The mask is not in the form 1**n,0**m */
135 return EINVAL;
136 }
137
138 *bits = 32 - zbits;
139 return EOK;
140}
141
142static uint32_t dhcp_uint32_decode(uint8_t *data)
143{
144 return
145 ((uint32_t)data[0] << 24) |
146 ((uint32_t)data[1] << 16) |
147 ((uint32_t)data[2] << 8) |
148 ((uint32_t)data[3]);
149}
150
151static errno_t dhcp_send_discover(dhcp_link_t *dlink)
152{
153 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
154 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
155
156 memset(msgbuf, 0, MAX_MSG_SIZE);
157 hdr->op = op_bootrequest;
158 hdr->htype = 1; /* AHRD_ETHERNET */
159 hdr->hlen = sizeof(addr48_t);
160 hdr->xid = host2uint32_t_be(42);
161 hdr->flags = flag_broadcast;
162
163 addr48(dlink->link_info.mac_addr, hdr->chaddr);
164 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
165
166 opt[0] = opt_msg_type;
167 opt[1] = 1;
168 opt[2] = msg_dhcpdiscover;
169 opt[3] = opt_end;
170
171 return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + 4);
172}
173
174static errno_t dhcp_send_request(dhcp_link_t *dlink, dhcp_offer_t *offer)
175{
176 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
177 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
178 size_t i;
179
180 memset(msgbuf, 0, MAX_MSG_SIZE);
181 hdr->op = op_bootrequest;
182 hdr->htype = 1; /* AHRD_ETHERNET */
183 hdr->hlen = 6;
184 hdr->xid = host2uint32_t_be(42);
185 hdr->flags = flag_broadcast;
186 hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
187 addr48(dlink->link_info.mac_addr, hdr->chaddr);
188 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
189
190 i = 0;
191
192 opt[i++] = opt_msg_type;
193 opt[i++] = 1;
194 opt[i++] = msg_dhcprequest;
195
196 opt[i++] = opt_req_ip_addr;
197 opt[i++] = 4;
198 opt[i++] = offer->oaddr.addr >> 24;
199 opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
200 opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
201 opt[i++] = offer->oaddr.addr & 0xff;
202
203 opt[i++] = opt_server_id;
204 opt[i++] = 4;
205 opt[i++] = offer->srv_addr.addr >> 24;
206 opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
207 opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
208 opt[i++] = offer->srv_addr.addr & 0xff;
209
210 opt[i++] = opt_end;
211
212 return dhcp_send(&dlink->dt, msgbuf, sizeof(dhcp_hdr_t) + i);
213}
214
215static errno_t dhcp_parse_reply(void *msg, size_t size, dhcp_offer_t *offer)
216{
217 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
218 inet_addr_t yiaddr;
219 inet_addr_t siaddr;
220 inet_addr_t giaddr;
221 uint32_t subnet_mask;
222 bool have_subnet_mask = false;
223 bool have_server_id = false;
224 int subnet_bits;
225 char *saddr;
226 uint8_t opt_type, opt_len;
227 uint8_t *msgb;
228 errno_t rc;
229 size_t i;
230
231 log_msg(LOG_DEFAULT, LVL_DEBUG, "Receive reply");
232 memset(offer, 0, sizeof(*offer));
233
234 inet_addr_set(uint32_t_be2host(hdr->yiaddr), &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 inet_addr_set(uint32_t_be2host(hdr->siaddr), &siaddr);
243 rc = inet_addr_format(&siaddr, &saddr);
244 if (rc != EOK)
245 return rc;
246
247 log_msg(LOG_DEFAULT, LVL_DEBUG, "Next server IP address: %s", saddr);
248 free(saddr);
249
250 inet_addr_set(uint32_t_be2host(hdr->giaddr), &giaddr);
251 rc = inet_addr_format(&giaddr, &saddr);
252 if (rc != EOK)
253 return rc;
254
255 log_msg(LOG_DEFAULT, LVL_DEBUG, "Relay agent IP address: %s", saddr);
256 free(saddr);
257
258 inet_naddr_set(yiaddr.addr, 0, &offer->oaddr);
259
260 msgb = (uint8_t *)msg;
261
262 i = sizeof(dhcp_hdr_t);
263 while (i < size) {
264 opt_type = msgb[i++];
265
266 if (opt_type == opt_pad)
267 continue;
268 if (opt_type == opt_end)
269 break;
270
271 if (i >= size)
272 return EINVAL;
273
274 opt_len = msgb[i++];
275
276 if (i + opt_len > size)
277 return EINVAL;
278
279 switch (opt_type) {
280 case opt_subnet_mask:
281 if (opt_len != 4)
282 return EINVAL;
283 subnet_mask = dhcp_uint32_decode(&msgb[i]);
284 rc = subnet_mask_decode(subnet_mask, &subnet_bits);
285 if (rc != EOK)
286 return EINVAL;
287 offer->oaddr.prefix = subnet_bits;
288 have_subnet_mask = true;
289 break;
290 case opt_msg_type:
291 if (opt_len != 1)
292 return EINVAL;
293 offer->msg_type = msgb[i];
294 break;
295 case opt_server_id:
296 if (opt_len != 4)
297 return EINVAL;
298 inet_addr_set(dhcp_uint32_decode(&msgb[i]),
299 &offer->srv_addr);
300 have_server_id = true;
301 break;
302 case opt_router:
303 if (opt_len != 4)
304 return EINVAL;
305 inet_addr_set(dhcp_uint32_decode(&msgb[i]),
306 &offer->router);
307 break;
308 case opt_dns_server:
309 if (opt_len < 4 || opt_len % 4 != 0)
310 return EINVAL;
311 /* XXX Handle multiple DNS servers properly */
312 inet_addr_set(dhcp_uint32_decode(&msgb[i]),
313 &offer->dns_server);
314 break;
315 case opt_end:
316 break;
317 default:
318 break;
319 }
320
321 /* Advance to the next option */
322 i = i + opt_len;
323 }
324
325 if (!have_server_id) {
326 log_msg(LOG_DEFAULT, LVL_ERROR, "Missing server ID option.");
327 return rc;
328 }
329
330 if (!have_subnet_mask) {
331 log_msg(LOG_DEFAULT, LVL_ERROR, "Missing subnet mask option.");
332 return rc;
333 }
334
335 rc = inet_naddr_format(&offer->oaddr, &saddr);
336 if (rc != EOK)
337 return rc;
338
339 log_msg(LOG_DEFAULT, LVL_DEBUG, "Offered network address: %s", saddr);
340 free(saddr);
341
342 if (offer->router.addr != 0) {
343 rc = inet_addr_format(&offer->router, &saddr);
344 if (rc != EOK)
345 return rc;
346
347 log_msg(LOG_DEFAULT, LVL_DEBUG, "Router address: %s", saddr);
348 free(saddr);
349 }
350
351 if (offer->dns_server.addr != 0) {
352 rc = inet_addr_format(&offer->dns_server, &saddr);
353 if (rc != EOK)
354 return rc;
355
356 log_msg(LOG_DEFAULT, LVL_DEBUG, "DNS server: %s", saddr);
357 free(saddr);
358 }
359
360 return EOK;
361}
362
363static errno_t dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
364{
365 errno_t rc;
366 service_id_t addr_id;
367 service_id_t sroute_id;
368 inet_naddr_t defr;
369
370 rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
371 &addr_id);
372 if (rc != EOK) {
373 log_msg(LOG_DEFAULT, LVL_ERROR,
374 "Error creating IP address %s: %s", "dhcp4a", str_error(rc));
375 return rc;
376 }
377
378 if (offer->router.addr != 0) {
379 inet_naddr_set(0, 0, &defr);
380
381 rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
382 if (rc != EOK) {
383 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating "
384 "default route %s: %s.", "dhcpdef", str_error(rc));
385 return rc;
386 }
387 }
388
389 if (offer->dns_server.addr != 0) {
390 rc = dnsr_set_srvaddr(&offer->dns_server);
391 if (rc != EOK) {
392 log_msg(LOG_DEFAULT, LVL_ERROR, "Error setting "
393 "nameserver address: %s)", str_error(rc));
394 return rc;
395 }
396 }
397
398 return EOK;
399}
400
401void dhcpsrv_links_init(void)
402{
403 list_initialize(&dhcp_links);
404}
405
406static dhcp_link_t *dhcpsrv_link_find(service_id_t link_id)
407{
408 list_foreach(dhcp_links, links, dhcp_link_t, dlink) {
409 if (dlink->link_id == link_id)
410 return dlink;
411 }
412
413 return NULL;
414}
415
416static void dhcp_link_set_failed(dhcp_link_t *dlink)
417{
418 log_msg(LOG_DEFAULT, LVL_NOTE, "Giving up on link %s",
419 dlink->link_info.name);
420 dlink->state = ds_fail;
421}
422
423static errno_t dhcp_discover_proc(dhcp_link_t *dlink)
424{
425 dlink->state = ds_selecting;
426
427 errno_t rc = dhcp_send_discover(dlink);
428 if (rc != EOK)
429 return EIO;
430
431 dlink->retries_left = dhcp_discover_retries;
432
433 if ((dlink->timeout->state == fts_not_set) ||
434 (dlink->timeout->state == fts_fired))
435 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
436 dhcpsrv_discover_timeout, dlink);
437
438 return rc;
439}
440
441errno_t dhcpsrv_link_add(service_id_t link_id)
442{
443 dhcp_link_t *dlink;
444 errno_t rc;
445
446 log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
447
448 if (dhcpsrv_link_find(link_id) != NULL) {
449 log_msg(LOG_DEFAULT, LVL_NOTE, "Link %zu already added",
450 link_id);
451 return EEXIST;
452 }
453
454 dlink = calloc(1, sizeof(dhcp_link_t));
455 if (dlink == NULL)
456 return ENOMEM;
457
458 dlink->link_id = link_id;
459 dlink->timeout = fibril_timer_create(NULL);
460 if (dlink->timeout == NULL) {
461 rc = ENOMEM;
462 goto error;
463 }
464
465 /* Get link hardware address */
466 rc = inetcfg_link_get(link_id, &dlink->link_info);
467 if (rc != EOK) {
468 log_msg(LOG_DEFAULT, LVL_ERROR, "Error getting properties "
469 "for link %zu.", link_id);
470 rc = EIO;
471 goto error;
472 }
473
474 rc = dhcp_transport_init(&dlink->dt, link_id, dhcpsrv_recv, dlink);
475 if (rc != EOK) {
476 log_msg(LOG_DEFAULT, LVL_ERROR, "Error initializing DHCP "
477 "transport for link %s.", dlink->link_info.name);
478 rc = EIO;
479 goto error;
480 }
481
482 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
483 rc = dhcp_discover_proc(dlink);
484 if (rc != EOK) {
485 log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER.");
486 dhcp_link_set_failed(dlink);
487 rc = EIO;
488 goto error;
489 }
490
491 list_append(&dlink->links, &dhcp_links);
492
493 return EOK;
494error:
495 if (dlink != NULL && dlink->timeout != NULL)
496 fibril_timer_destroy(dlink->timeout);
497 free(dlink);
498 return rc;
499}
500
501errno_t dhcpsrv_link_remove(service_id_t link_id)
502{
503 return ENOTSUP;
504}
505
506errno_t dhcpsrv_discover(service_id_t link_id)
507{
508 log_msg(LOG_DEFAULT, LVL_DEBUG, "dhcpsrv_link_add(%zu)", link_id);
509
510 dhcp_link_t *dlink = dhcpsrv_link_find(link_id);
511
512 if (dlink == NULL) {
513 log_msg(LOG_DEFAULT, LVL_NOTE, "Link %zu doesn't exist",
514 link_id);
515 return EINVAL;
516 }
517
518 return dhcp_discover_proc(dlink);
519}
520
521static void dhcpsrv_recv_offer(dhcp_link_t *dlink, dhcp_offer_t *offer)
522{
523 errno_t rc;
524
525 if (dlink->state != ds_selecting) {
526 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received offer in state "
527 " %d, ignoring.", (int)dlink->state);
528 return;
529 }
530
531 fibril_timer_clear(dlink->timeout);
532 dlink->offer = *offer;
533 dlink->state = ds_requesting;
534
535 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
536 rc = dhcp_send_request(dlink, offer);
537 if (rc != EOK) {
538 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
539 return;
540 }
541
542 dlink->retries_left = dhcp_request_retries;
543 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
544 dhcpsrv_request_timeout, dlink);
545}
546
547static void dhcpsrv_recv_ack(dhcp_link_t *dlink, dhcp_offer_t *offer)
548{
549 errno_t rc;
550
551 if (dlink->state != ds_requesting) {
552 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received ack in state "
553 " %d, ignoring.", (int)dlink->state);
554 return;
555 }
556
557 fibril_timer_clear(dlink->timeout);
558 dlink->offer = *offer;
559 dlink->state = ds_bound;
560
561 rc = dhcp_cfg_create(dlink->link_id, offer);
562 if (rc != EOK) {
563 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error creating configuration.");
564 return;
565 }
566
567 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: Successfully configured.",
568 dlink->link_info.name);
569}
570
571static void dhcpsrv_recv(void *arg, void *msg, size_t size)
572{
573 dhcp_link_t *dlink = (dhcp_link_t *)arg;
574 dhcp_offer_t offer;
575 errno_t rc;
576
577 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: dhcpsrv_recv() %zu bytes",
578 dlink->link_info.name, size);
579
580 rc = dhcp_parse_reply(msg, size, &offer);
581 if (rc != EOK) {
582 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error parsing reply");
583 return;
584 }
585
586 switch (offer.msg_type) {
587 case msg_dhcpoffer:
588 dhcpsrv_recv_offer(dlink, &offer);
589 break;
590 case msg_dhcpack:
591 dhcpsrv_recv_ack(dlink, &offer);
592 break;
593 default:
594 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received unexpected "
595 "message type. %d", (int)offer.msg_type);
596 break;
597 }
598}
599
600static void dhcpsrv_discover_timeout(void *arg)
601{
602 dhcp_link_t *dlink = (dhcp_link_t *)arg;
603 errno_t rc;
604
605 assert(dlink->state == ds_selecting);
606 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_discover_timeout",
607 dlink->link_info.name);
608
609 if (dlink->retries_left == 0) {
610 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
611 dhcp_link_set_failed(dlink);
612 return;
613 }
614 --dlink->retries_left;
615
616 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPDISCOVER");
617 rc = dhcp_send_discover(dlink);
618 if (rc != EOK) {
619 log_msg(LOG_DEFAULT, LVL_ERROR, "Error sending DHCPDISCOVER");
620 dhcp_link_set_failed(dlink);
621 return;
622 }
623
624 fibril_timer_set(dlink->timeout, dhcp_discover_timeout_val,
625 dhcpsrv_discover_timeout, dlink);
626}
627
628static void dhcpsrv_request_timeout(void *arg)
629{
630 dhcp_link_t *dlink = (dhcp_link_t *)arg;
631 errno_t rc;
632
633 assert(dlink->state == ds_requesting);
634 log_msg(LOG_DEFAULT, LVL_NOTE, "%s: dcpsrv_request_timeout",
635 dlink->link_info.name);
636
637 if (dlink->retries_left == 0) {
638 log_msg(LOG_DEFAULT, LVL_NOTE, "Retries exhausted");
639 dhcp_link_set_failed(dlink);
640 return;
641 }
642 --dlink->retries_left;
643
644 log_msg(LOG_DEFAULT, LVL_DEBUG, "Send DHCPREQUEST");
645 rc = dhcp_send_request(dlink, &dlink->offer);
646 if (rc != EOK) {
647 log_msg(LOG_DEFAULT, LVL_DEBUG, "Error sending request.");
648 dhcp_link_set_failed(dlink);
649 return;
650 }
651
652 fibril_timer_set(dlink->timeout, dhcp_request_timeout_val,
653 dhcpsrv_request_timeout, dlink);
654}
655
656/** @}
657 */
Note: See TracBrowser for help on using the repository browser.