source: mainline/uspace/srv/net/dhcp/dhcp.c@ 947e2ef

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

Determine MAC address for DHCP automatically.

  • Property mode set to 100644
File size: 10.6 KB
RevLine 
[695b6ff]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 <bitops.h>
38#include <inet/addr.h>
39#include <inet/dnsr.h>
40#include <inet/inetcfg.h>
41#include <loc.h>
42#include <net/in.h>
43#include <net/inet.h>
44#include <net/socket.h>
45#include <stdio.h>
46#include <stdlib.h>
47
48#include "dhcp_std.h"
49
50#define NAME "dhcp"
51
52#define MAX_MSG_SIZE 1024
53
54static int transport_fd = -1;
[947e2ef]55static inet_link_info_t link_info;
[695b6ff]56static uint8_t msgbuf[MAX_MSG_SIZE];
57
58typedef struct {
59 /** Message type */
60 enum dhcp_msg_type msg_type;
61 /** Offered address */
62 inet_naddr_t oaddr;
63 /** Server address */
64 inet_addr_t srv_addr;
65 /** Router address */
66 inet_addr_t router;
67 /** DNS server */
68 inet_addr_t dns_server;
69} dhcp_offer_t;
70
71/** Decode subnet mask into subnet prefix length. */
72static int subnet_mask_decode(uint32_t mask, int *bits)
73{
74 int zbits;
75 uint32_t nmask;
76
77 if (mask == 0xffffffff) {
78 *bits = 32;
79 return EOK;
80 }
81
82 zbits = 1 + fnzb32(mask ^ 0xffffffff);
83 nmask = BIT_RRANGE(uint32_t, zbits);
84
85 if ((mask ^ nmask) != 0xffffffff) {
86 /* The mask is not in the form 1**n,0**m */
87 return EINVAL;
88 }
89
90 *bits = 32 - zbits;
91 return EOK;
92}
93
94static uint32_t dhcp_uint32_decode(uint8_t *data)
95{
96 return
97 ((uint32_t)data[0] << 24) |
98 ((uint32_t)data[1] << 16) |
99 ((uint32_t)data[2] << 8) |
100 ((uint32_t)data[3]);
101}
102
103static int dhcp_send(void *msg, size_t size)
104{
105 struct sockaddr_in addr;
106 int rc;
107
108 addr.sin_family = AF_INET;
109 addr.sin_port = htons(dhcp_server_port);
110 addr.sin_addr.s_addr = htonl(addr32_broadcast_all_hosts);
111
112 rc = sendto(transport_fd, msg, size, 0,
113 (struct sockaddr *)&addr, sizeof(addr));
114 if (rc != EOK) {
115 printf("Sending failed\n");
116 return rc;
117 }
118
119 return EOK;
120}
121
122static int dhcp_send_discover(void)
123{
124 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
125 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
126
127 memset(msgbuf, 0, MAX_MSG_SIZE);
128 hdr->op = op_bootrequest;
129 hdr->htype = 1; /* AHRD_ETHERNET */
130 hdr->hlen = sizeof(addr48_t);
131 hdr->xid = host2uint32_t_be(42);
132 hdr->flags = flag_broadcast;
133
[947e2ef]134 addr48(link_info.mac_addr, hdr->chaddr);
[695b6ff]135 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
136
137 opt[0] = opt_msg_type;
138 opt[1] = 1;
139 opt[2] = msg_dhcpdiscover;
140 opt[3] = opt_end;
141
142 return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + 4);
143}
144
145static int dhcp_recv_msg(void **rmsg, size_t *rsize)
146{
147 struct sockaddr_in src_addr;
148 socklen_t src_addr_size;
149 size_t recv_size;
150 int rc;
151
152 src_addr_size = sizeof(src_addr);
153 rc = recvfrom(transport_fd, msgbuf, MAX_MSG_SIZE, 0,
154 (struct sockaddr *)&src_addr, &src_addr_size);
155 if (rc < 0) {
156 printf("recvfrom failed (%d)\n", rc);
157 return rc;
158 }
159
160 recv_size = (size_t)rc;
161 *rmsg = msgbuf;
162 *rsize = recv_size;
163
164 return EOK;
165}
166
167static int dhcp_send_request(dhcp_offer_t *offer)
168{
169 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msgbuf;
170 uint8_t *opt = msgbuf + sizeof(dhcp_hdr_t);
171 size_t i;
172
173 memset(msgbuf, 0, MAX_MSG_SIZE);
174 hdr->op = op_bootrequest;
175 hdr->htype = 1; /* AHRD_ETHERNET */
176 hdr->hlen = 6;
177 hdr->xid = host2uint32_t_be(42);
178 hdr->flags = flag_broadcast;
179 hdr->ciaddr = host2uint32_t_be(offer->oaddr.addr);
[947e2ef]180 addr48(link_info.mac_addr, hdr->chaddr);
[695b6ff]181 hdr->opt_magic = host2uint32_t_be(dhcp_opt_magic);
182
183 i = 0;
184
185 opt[i++] = opt_msg_type;
186 opt[i++] = 1;
187 opt[i++] = msg_dhcprequest;
188
189 opt[i++] = opt_req_ip_addr;
190 opt[i++] = 4;
191 opt[i++] = offer->oaddr.addr >> 24;
192 opt[i++] = (offer->oaddr.addr >> 16) & 0xff;
193 opt[i++] = (offer->oaddr.addr >> 8) & 0xff;
194 opt[i++] = offer->oaddr.addr & 0xff;
195
196 opt[i++] = opt_server_id;
197 opt[i++] = 4;
198 opt[i++] = offer->srv_addr.addr >> 24;
199 opt[i++] = (offer->srv_addr.addr >> 16) & 0xff;
200 opt[i++] = (offer->srv_addr.addr >> 8) & 0xff;
201 opt[i++] = offer->srv_addr.addr & 0xff;
202
203 opt[i++] = opt_end;
204
205 return dhcp_send(msgbuf, sizeof(dhcp_hdr_t) + i);
206}
207
208static int dhcp_recv_reply(void *msg, size_t size, dhcp_offer_t *offer)
209{
210 dhcp_hdr_t *hdr = (dhcp_hdr_t *)msg;
211 inet_addr_t yiaddr;
212 inet_addr_t siaddr;
213 inet_addr_t giaddr;
214 uint32_t subnet_mask;
215 bool have_subnet_mask = false;
216 bool have_server_id = false;
217 int subnet_bits;
218 char *saddr;
219 uint8_t opt_type, opt_len;
220 uint8_t *msgb;
221 int rc;
222 size_t i;
223
224 printf("Receive reply\n");
225 memset(offer, 0, sizeof(*offer));
226
227 yiaddr.family = AF_INET;
228 yiaddr.addr = uint32_t_be2host(hdr->yiaddr);
229 rc = inet_addr_format(&yiaddr, &saddr);
230 if (rc != EOK)
231 return rc;
232
233 printf("Your IP address: %s\n", saddr);
234 free(saddr);
235
236 siaddr.family = AF_INET;
237 siaddr.addr = uint32_t_be2host(hdr->siaddr);
238 rc = inet_addr_format(&siaddr, &saddr);
239 if (rc != EOK)
240 return rc;
241
242 printf("Next server IP address: %s\n", saddr);
243 free(saddr);
244
245 giaddr.family = AF_INET;
246 giaddr.addr = uint32_t_be2host(hdr->giaddr);
247 rc = inet_addr_format(&giaddr, &saddr);
248 if (rc != EOK)
249 return rc;
250
251 printf("Relay agent IP address: %s\n", saddr);
252 free(saddr);
253
254 offer->oaddr.family = AF_INET;
255 offer->oaddr.addr = yiaddr.addr;
256
257 msgb = (uint8_t *)msg;
258
259 i = sizeof(dhcp_hdr_t);
260 while (i < size) {
261 opt_type = msgb[i++];
262
263 if (opt_type == opt_pad)
264 continue;
265 if (opt_type == opt_end)
266 break;
267
268 if (i >= size)
269 return EINVAL;
270
271 opt_len = msgb[i++];
272
273 if (i + opt_len > size)
274 return EINVAL;
275
276 switch (opt_type) {
277 case opt_subnet_mask:
278 if (opt_len != 4)
279 return EINVAL;
280 subnet_mask = dhcp_uint32_decode(&msgb[i]);
281 rc = subnet_mask_decode(subnet_mask, &subnet_bits);
282 if (rc != EOK)
283 return EINVAL;
284 offer->oaddr.prefix = subnet_bits;
285 have_subnet_mask = true;
286 break;
287 case opt_msg_type:
288 if (opt_len != 1)
289 return EINVAL;
290 offer->msg_type = msgb[i];
291 break;
292 case opt_server_id:
293 if (opt_len != 4)
294 return EINVAL;
295 offer->srv_addr.family = AF_INET;
296 offer->srv_addr.addr = dhcp_uint32_decode(&msgb[i]);
297 have_server_id = true;
298 break;
299 case opt_router:
300 if (opt_len != 4)
301 return EINVAL;
302 offer->router.family = AF_INET;
303 offer->router.addr = dhcp_uint32_decode(&msgb[i]);
304 break;
305 case opt_dns_server:
306 if (opt_len != 4)
307 return EINVAL;
308 offer->dns_server.family = AF_INET;
309 offer->dns_server.addr = dhcp_uint32_decode(&msgb[i]);
310 break;
311 case opt_end:
312 break;
313 default:
314 break;
315 }
316
317 /* Advance to the next option */
318 i = i + opt_len;
319 }
320
321 if (!have_server_id) {
322 printf("Missing server ID option.\n");
323 return rc;
324 }
325
326 if (!have_subnet_mask) {
327 printf("Missing subnet mask option.\n");
328 return rc;
329 }
330
331 rc = inet_naddr_format(&offer->oaddr, &saddr);
332 if (rc != EOK)
333 return rc;
334
335 printf("Offered network address: %s\n", saddr);
336 free(saddr);
337
338 if (offer->router.addr != 0) {
339 rc = inet_addr_format(&offer->router, &saddr);
340 if (rc != EOK)
341 return rc;
342
343 printf("Router address: %s\n", saddr);
344 free(saddr);
345 }
346
347 if (offer->dns_server.addr != 0) {
348 rc = inet_addr_format(&offer->dns_server, &saddr);
349 if (rc != EOK)
350 return rc;
351
352 printf("DNS server: %s\n", saddr);
353 free(saddr);
354 }
355
356 return EOK;
357}
358
359static int dhcp_cfg_create(service_id_t iplink, dhcp_offer_t *offer)
360{
361 int rc;
362 service_id_t addr_id;
363 service_id_t sroute_id;
364 inet_naddr_t defr;
365
366 rc = inetcfg_addr_create_static("dhcp4a", &offer->oaddr, iplink,
367 &addr_id);
368 if (rc != EOK) {
369 printf("Error creating IP address %s (%d)\n", "dhcp4a", rc);
370 return rc;
371 }
372
373 if (offer->router.addr != 0) {
374 defr.family = AF_INET;
375 defr.addr = 0;
376 defr.prefix = 0;
377
378 rc = inetcfg_sroute_create("dhcpdef", &defr, &offer->router, &sroute_id);
379 if (rc != EOK) {
380 printf("Error creating default route %s (%d).\n", "dhcpdef",
381 rc);
382 return rc;
383 }
384 }
385
386 if (offer->dns_server.addr != 0) {
387 rc = dnsr_set_srvaddr(&offer->dns_server);
388 if (rc != EOK) {
[947e2ef]389 printf("%s: Error setting nameserver address (%d))\n",
[695b6ff]390 NAME, rc);
391 return rc;
392 }
393 }
394
395 return EOK;
396}
397
398int main(int argc, char *argv[])
399{
400 int fd;
401 struct sockaddr_in laddr;
402 void *msg;
403 service_id_t iplink;
404 size_t msg_size;
405 dhcp_offer_t offer;
406 int rc;
407
408 if (argc < 2) {
409 printf("syntax: %s <ip-link>\n", NAME);
410 return 1;
411 }
412
413 rc = inetcfg_init();
414 if (rc != EOK) {
[947e2ef]415 printf("Error contacting inet configuration service.\n");
[695b6ff]416 return 1;
417 }
418
419 rc = loc_service_get_id(argv[1], &iplink, 0);
420 if (rc != EOK) {
[947e2ef]421 printf("Error resolving service '%s'.\n", argv[1]);
[695b6ff]422 return 1;
423 }
424
[947e2ef]425 /* Get link hardware address */
426 rc = inetcfg_link_get(iplink, &link_info);
427 if (rc != EOK) {
428 printf("Error getting properties for link '%s'.\n", argv[1]);
429 return 1;
430 }
[695b6ff]431
432 laddr.sin_family = AF_INET;
433 laddr.sin_port = htons(dhcp_client_port);
434 laddr.sin_addr.s_addr = INADDR_ANY;
435
436 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
437 if (fd < 0)
438 return 1;
439
440 printf("Bind socket.\n");
441 rc = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
442 if (rc != EOK)
443 return 1;
444
445 printf("Set socket options\n");
446 rc = setsockopt(fd, SOL_SOCKET, SO_IPLINK, &iplink, sizeof(iplink));
447 if (rc != EOK)
448 return 1;
449
450 transport_fd = fd;
451
452 printf("Send DHCPDISCOVER\n");
453 rc = dhcp_send_discover();
454 if (rc != EOK)
455 return 1;
456
457 rc = dhcp_recv_msg(&msg, &msg_size);
458 if (rc != EOK)
459 return 1;
460
461 printf("Received %zu bytes\n", msg_size);
462
463 rc = dhcp_recv_reply(msg, msg_size, &offer);
464 if (rc != EOK)
465 return 1;
466
467 rc = dhcp_send_request(&offer);
468 if (rc != EOK)
469 return 1;
470
471 rc = dhcp_recv_msg(&msg, &msg_size);
472 if (rc != EOK)
473 return 1;
474
475 printf("Received %zu bytes\n", msg_size);
476
477 rc = dhcp_recv_reply(msg, msg_size, &offer);
478 if (rc != EOK)
479 return 1;
480
481 rc = dhcp_cfg_create(iplink, &offer);
482 if (rc != EOK)
483 return 1;
484
485 closesocket(fd);
486 return 0;
487}
488
489/** @}
490 */
Note: See TracBrowser for help on using the repository browser.