VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/bootp.c@ 28587

Last change on this file since 28587 was 28449, checked in by vboxsync, 15 years ago

NAT: slirp file headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.7 KB
Line 
1/* $Id: bootp.c 28449 2010-04-19 09:52:59Z vboxsync $ */
2/** @file
3 * NAT - BOOTP/DHCP server emulation.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*
23 * This code is based on:
24 *
25 * QEMU BOOTP/DHCP server
26 *
27 * Copyright (c) 2004 Fabrice Bellard
28 *
29 * Permission is hereby granted, free of charge, to any person obtaining a copy
30 * of this software and associated documentation files (the "Software"), to deal
31 * in the Software without restriction, including without limitation the rights
32 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33 * copies of the Software, and to permit persons to whom the Software is
34 * furnished to do so, subject to the following conditions:
35 *
36 * The above copyright notice and this permission notice shall be included in
37 * all copies or substantial portions of the Software.
38 *
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
42 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45 * THE SOFTWARE.
46 */
47#include <slirp.h>
48
49/** Entry in the table of known DHCP clients. */
50typedef struct
51{
52 uint32_t xid;
53 bool allocated;
54 uint8_t macaddr[6];
55 struct in_addr addr;
56 int number;
57} BOOTPClient;
58/** Number of DHCP clients supported by NAT. */
59#define NB_ADDR 16
60
61#define bootp_clients ((BOOTPClient *)pData->pbootp_clients)
62
63/* XXX: only DHCP is supported */
64static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
65
66static void bootp_reply(PNATState pData, struct mbuf *m0, int offReply, uint16_t flags);
67
68static uint8_t *dhcp_find_option(uint8_t *vend, uint8_t tag)
69{
70 uint8_t *q = vend;
71 uint8_t len;
72 /*@todo magic validation */
73 q += 4; /*magic*/
74 while(*q != RFC1533_END)
75 {
76 if (*q == RFC1533_PAD)
77 continue;
78 if (*q == tag)
79 return q;
80 q++;
81 len = *q;
82 q += 1 + len;
83 }
84 return NULL;
85}
86
87static BOOTPClient *bc_alloc_client(PNATState pData)
88{
89 int i;
90 for (i = 0; i < NB_ADDR; i++)
91 {
92 if (!bootp_clients[i].allocated)
93 {
94 BOOTPClient *bc;
95
96 bc = &bootp_clients[i];
97 memset(bc, 0, sizeof(BOOTPClient));
98 bc->allocated = 1;
99 bc->number = i;
100 return bc;
101 }
102 }
103 return NULL;
104}
105
106static BOOTPClient *get_new_addr(PNATState pData, struct in_addr *paddr)
107{
108 BOOTPClient *bc;
109 bc = bc_alloc_client(pData);
110 if (!bc)
111 return NULL;
112
113 paddr->s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | (bc->number + START_ADDR));
114 bc->addr.s_addr = paddr->s_addr;
115 return bc;
116}
117
118static int release_addr(PNATState pData, struct in_addr *paddr)
119{
120 unsigned i;
121 for (i = 0; i < NB_ADDR; i++)
122 {
123 if (paddr->s_addr == bootp_clients[i].addr.s_addr)
124 {
125 memset(&bootp_clients[i], 0, sizeof(BOOTPClient));
126 return VINF_SUCCESS;
127 }
128 }
129 return VERR_NOT_FOUND;
130}
131
132/*
133 * from RFC 2131 4.3.1
134 * Field DHCPOFFER DHCPACK DHCPNAK
135 * ----- --------- ------- -------
136 * 'op' BOOTREPLY BOOTREPLY BOOTREPLY
137 * 'htype' (From "Assigned Numbers" RFC)
138 * 'hlen' (Hardware address length in octets)
139 * 'hops' 0 0 0
140 * 'xid' 'xid' from client 'xid' from client 'xid' from client
141 * DHCPDISCOVER DHCPREQUEST DHCPREQUEST
142 * message message message
143 * 'secs' 0 0 0
144 * 'ciaddr' 0 'ciaddr' from 0
145 * DHCPREQUEST or 0
146 * 'yiaddr' IP address offered IP address 0
147 * to client assigned to client
148 * 'siaddr' IP address of next IP address of next 0
149 * bootstrap server bootstrap server
150 * 'flags' 'flags' from 'flags' from 'flags' from
151 * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
152 * message message message
153 * 'giaddr' 'giaddr' from 'giaddr' from 'giaddr' from
154 * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
155 * message message message
156 * 'chaddr' 'chaddr' from 'chaddr' from 'chaddr' from
157 * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
158 * message message message
159 * 'sname' Server host name Server host name (unused)
160 * or options or options
161 * 'file' Client boot file Client boot file (unused)
162 * name or options name or options
163 * 'options' options options
164 *
165 * Option DHCPOFFER DHCPACK DHCPNAK
166 * ------ --------- ------- -------
167 * Requested IP address MUST NOT MUST NOT MUST NOT
168 * IP address lease time MUST MUST (DHCPREQUEST) MUST NOT
169 * MUST NOT (DHCPINFORM)
170 * Use 'file'/'sname' fields MAY MAY MUST NOT
171 * DHCP message type DHCPOFFER DHCPACK DHCPNAK
172 * Parameter request list MUST NOT MUST NOT MUST NOT
173 * Message SHOULD SHOULD SHOULD
174 * Client identifier MUST NOT MUST NOT MAY
175 * Vendor class identifier MAY MAY MAY
176 * Server identifier MUST MUST MUST
177 * Maximum message size MUST NOT MUST NOT MUST NOT
178 * All others MAY MAY MUST NOT
179 */
180static BOOTPClient *find_addr(PNATState pData, struct in_addr *paddr, const uint8_t *macaddr)
181{
182 int i;
183
184 for (i = 0; i < NB_ADDR; i++)
185 {
186 if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
187 {
188 BOOTPClient *bc;
189
190 bc = &bootp_clients[i];
191 bc->allocated = 1;
192 paddr->s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | (i + START_ADDR));
193 return bc;
194 }
195 }
196 return NULL;
197}
198
199static struct mbuf *dhcp_create_msg(PNATState pData, struct bootp_t *bp, struct mbuf *m, uint8_t type)
200{
201 struct bootp_t *rbp;
202 struct ethhdr *eh;
203 uint8_t *q;
204
205 eh = mtod(m, struct ethhdr *);
206 memcpy(eh->h_source, bp->bp_hwaddr, ETH_ALEN); /* XXX: if_encap just swap source with dest*/
207
208 m->m_data += if_maxlinkhdr; /*reserve ether header */
209
210 rbp = mtod(m, struct bootp_t *);
211 memset(rbp, 0, sizeof(struct bootp_t));
212 rbp->bp_op = BOOTP_REPLY;
213 rbp->bp_xid = bp->bp_xid; /* see table 3 of rfc2131*/
214 rbp->bp_flags = bp->bp_flags; /* figure 2 of rfc2131 */
215 rbp->bp_giaddr.s_addr = bp->bp_giaddr.s_addr;
216#if 0 /*check flags*/
217 saddr.sin_port = RT_H2N_U16_C(BOOTP_SERVER);
218 daddr.sin_port = RT_H2N_U16_C(BOOTP_CLIENT);
219#endif
220 rbp->bp_htype = 1;
221 rbp->bp_hlen = 6;
222 memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
223
224 memcpy(rbp->bp_vend, rfc1533_cookie, 4); /* cookie */
225 q = rbp->bp_vend;
226 q += 4;
227 *q++ = RFC2132_MSG_TYPE;
228 *q++ = 1;
229 *q++ = type;
230
231 return m;
232}
233
234static int dhcp_do_ack_offer(PNATState pData, struct mbuf *m, BOOTPClient *bc, int fDhcpRequest)
235{
236 struct bootp_t *rbp = NULL;
237 uint8_t *q;
238 struct in_addr saddr;
239 int val;
240
241 struct dns_entry *de = NULL;
242 struct dns_domain_entry *dd = NULL;
243 int added = 0;
244 uint8_t *q_dns_header = NULL;
245 uint32_t lease_time = RT_H2N_U32_C(LEASE_TIME);
246 uint32_t netmask = RT_H2N_U32(pData->netmask);
247
248 rbp = mtod(m, struct bootp_t *);
249 q = &rbp->bp_vend[0];
250 q += 7; /* !cookie rfc 2132 + TYPE*/
251
252 /*DHCP Offer specific*/
253 if ( tftp_prefix
254 && RTDirExists(tftp_prefix)
255 && bootp_filename)
256 RTStrPrintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
257
258 Log(("NAT: DHCP: bp_file:%s\n", &rbp->bp_file));
259 /* Address/port of the DHCP server. */
260 rbp->bp_yiaddr = bc->addr; /* Client IP address */
261 Log(("NAT: DHCP: bp_yiaddr:%R[IP4]\n", &rbp->bp_yiaddr));
262 rbp->bp_siaddr = pData->tftp_server; /* Next Server IP address, i.e. TFTP */
263 Log(("NAT: DHCP: bp_siaddr:%R[IP4]\n", &rbp->bp_siaddr));
264 if (fDhcpRequest)
265 {
266 rbp->bp_ciaddr.s_addr = bc->addr.s_addr; /* Client IP address */
267 }
268#ifndef VBOX_WITH_NAT_SERVICE
269 saddr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
270#else
271 saddr.s_addr = pData->special_addr.s_addr;
272#endif
273 Log(("NAT: DHCP: s_addr:%R[IP4]\n", &saddr));
274
275#define FILL_BOOTP_EXT(q, tag, len, pvalue) \
276 do { \
277 struct bootp_ext *be = (struct bootp_ext *)(q); \
278 be->bpe_tag = (tag); \
279 be->bpe_len = (len); \
280 memcpy(&be[1], (pvalue), (len)); \
281 (q) = (uint8_t *)(&be[1]) + (len); \
282 }while(0)
283/* appending another value to tag, calculates len of whole block*/
284#define FILL_BOOTP_APP(head, q, tag, len, pvalue) \
285 do { \
286 struct bootp_ext *be = (struct bootp_ext *)(head); \
287 memcpy(q, (pvalue), (len)); \
288 (q) += (len); \
289 Assert(be->bpe_tag == (tag)); \
290 be->bpe_len += (len); \
291 }while(0)
292
293
294 FILL_BOOTP_EXT(q, RFC1533_NETMASK, 4, &netmask);
295 FILL_BOOTP_EXT(q, RFC1533_GATEWAY, 4, &saddr);
296
297 if (pData->fUseDnsProxy || pData->fUseHostResolver)
298 {
299 uint32_t addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
300 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &addr);
301 goto skip_dns_servers;
302 }
303
304 if (!TAILQ_EMPTY(&pData->pDnsList))
305 {
306 de = TAILQ_LAST(&pData->pDnsList, dns_list_head);
307 q_dns_header = q;
308 FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &de->de_addr.s_addr);
309 }
310
311 TAILQ_FOREACH_REVERSE(de, &pData->pDnsList, dns_list_head, de_list)
312 {
313 if (TAILQ_LAST(&pData->pDnsList, dns_list_head) == de)
314 continue; /* first value with head we've ingected before */
315 FILL_BOOTP_APP(q_dns_header, q, RFC1533_DNS, 4, &de->de_addr.s_addr);
316 }
317
318skip_dns_servers:
319 if (LIST_EMPTY(&pData->pDomainList))
320 {
321 /* Microsoft dhcp client doen't like domain-less dhcp and trimmed packets*/
322 /* dhcpcd client very sad if no domain name is passed */
323 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, " ");
324 }
325 if (pData->fPassDomain && !pData->fUseHostResolver)
326 {
327 LIST_FOREACH(dd, &pData->pDomainList, dd_list)
328 {
329
330 if (dd->dd_pszDomain == NULL)
331 continue;
332 /* never meet valid separator here in RFC1533*/
333 if (added != 0)
334 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, ",");
335 else
336 added = 1;
337 val = (int)strlen(dd->dd_pszDomain);
338 FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, val, dd->dd_pszDomain);
339 }
340 }
341
342 FILL_BOOTP_EXT(q, RFC2132_LEASE_TIME, 4, &lease_time);
343
344 if (*slirp_hostname)
345 {
346 val = (int)strlen(slirp_hostname);
347 FILL_BOOTP_EXT(q, RFC1533_HOSTNAME, val, slirp_hostname);
348 }
349 slirp_arp_cache_update_or_add(pData, rbp->bp_yiaddr.s_addr, bc->macaddr);
350 return q - rbp->bp_vend; /*return offset */
351}
352
353static int dhcp_send_nack(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m)
354{
355 struct bootp_t *rbp;
356 uint8_t *q = NULL;
357 rbp = mtod(m, struct bootp_t *);
358
359 dhcp_create_msg(pData, bp, m, DHCPNAK);
360 return 7;
361}
362
363static int dhcp_send_ack(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m, int fDhcpRequest)
364{
365 int offReply = 0; /* boot_reply will fill general options and add END before sending response */
366
367 dhcp_create_msg(pData, bp, m, DHCPACK);
368 offReply = dhcp_do_ack_offer(pData, m, bc, fDhcpRequest);
369 return offReply;
370}
371
372static int dhcp_send_offer(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m)
373{
374 int offReply = 0; /* boot_reply will fill general options and add END before sending response */
375
376 dhcp_create_msg(pData, bp, m, DHCPOFFER);
377 offReply = dhcp_do_ack_offer(pData, m, bc, /* fDhcpRequest=*/ 0);
378 return offReply;
379}
380
381/**
382 * decoding client messages RFC2131 (4.3.6)
383 * ---------------------------------------------------------------------
384 * | |INIT-REBOOT |SELECTING |RENEWING |REBINDING |
385 * ---------------------------------------------------------------------
386 * |broad/unicast |broadcast |broadcast |unicast |broadcast |
387 * |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT |
388 * |requested-ip |MUST |MUST |MUST NOT |MUST NOT |
389 * |ciaddr |zero |zero |IP address |IP address|
390 * ---------------------------------------------------------------------
391 *
392 */
393
394enum DHCP_REQUEST_STATES
395{
396 INIT_REBOOT,
397 SELECTING,
398 RENEWING,
399 REBINDING,
400 NONE
401};
402
403static int dhcp_decode_request(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size, struct mbuf *m)
404{
405 BOOTPClient *bc = NULL;
406 struct in_addr daddr;
407 int offReply;
408 uint8_t *req_ip = NULL;
409 uint8_t *server_ip = NULL;
410 uint32_t ui32;
411 enum DHCP_REQUEST_STATES dhcp_stat = NONE;
412
413 /* need to understand which type of request we get */
414 req_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
415 server_ip = dhcp_find_option(&bp->bp_vend[0], RFC2132_SRV_ID);
416 bc = find_addr(pData, &daddr, bp->bp_hwaddr);
417
418 if (server_ip != NULL)
419 {
420 /* selecting */
421 if (!bc)
422 {
423 LogRel(("NAT: DHCP no IP was allocated\n"));
424 return -1;
425 }
426
427 dhcp_stat = SELECTING;
428 Assert((bp->bp_ciaddr.s_addr == INADDR_ANY));
429 Assert((*(uint32_t *)(req_ip + 2) == bc->addr.s_addr)); /*the same address as in offer*/
430#if 0
431 /* DSL xid in request differ from offer */
432 Assert((bp->bp_xid == bc->xid));
433#endif
434 }
435 else
436 {
437 if (req_ip != NULL)
438 {
439 /* init-reboot */
440 dhcp_stat = INIT_REBOOT;
441 }
442 else
443 {
444 /* table 4 of rfc2131 */
445 if (bp->bp_flags & RT_H2N_U16_C(DHCP_FLAGS_B))
446 dhcp_stat = REBINDING;
447 else
448 dhcp_stat = RENEWING;
449 }
450 }
451
452 /*?? renewing ??*/
453 switch (dhcp_stat)
454 {
455 case RENEWING:
456 Assert((server_ip == NULL && req_ip == NULL && bp->bp_ciaddr.s_addr != INADDR_ANY));
457 if (bc != NULL)
458 {
459 Assert((bc->addr.s_addr == bp->bp_ciaddr.s_addr));
460 /*if it already here well just do ack, we aren't aware of dhcp time expiration*/
461 }
462 else
463 {
464 if ((bp->bp_ciaddr.s_addr & RT_H2N_U32(pData->netmask)) != pData->special_addr.s_addr)
465 {
466 LogRel(("NAT: Client %R[IP4] requested IP -- sending NAK\n", &bp->bp_ciaddr));
467 offReply = dhcp_send_nack(pData, bp, bc, m);
468 return offReply;
469 }
470
471 bc = bc_alloc_client(pData);
472 if (!bc)
473 {
474 LogRel(("NAT: can't alloc address. RENEW has been silently ignored.\n"));
475 return -1;
476 }
477
478 Assert((bp->bp_hlen == ETH_ALEN));
479 memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
480 bc->addr.s_addr = bp->bp_ciaddr.s_addr;
481 }
482 break;
483
484 case INIT_REBOOT:
485 Assert(server_ip == NULL);
486 Assert(req_ip != NULL);
487 ui32 = *(uint32_t *)(req_ip + 2);
488 if ((ui32 & RT_H2N_U32(pData->netmask)) != pData->special_addr.s_addr)
489 {
490 LogRel(("NAT: address %R[IP4] has been requested -- sending NAK\n", &ui32));
491 offReply = dhcp_send_nack(pData, bp, bc, m);
492 return offReply;
493 }
494
495 bc = bc_alloc_client(pData);
496 if (!bc)
497 {
498 LogRel(("NAT: can't alloc address. RENEW has been silently ignored\n"));
499 return -1;
500 }
501 Assert((bp->bp_hlen == ETH_ALEN));
502 memcpy(bc->macaddr, bp->bp_hwaddr, bp->bp_hlen);
503 bc->addr.s_addr = ui32;
504 break;
505
506 case NONE:
507 Assert((dhcp_stat != NONE));
508 return -1;
509
510 default:
511 break;
512 }
513
514 LogRel(("NAT: DHCP offered IP address %R[IP4]\n", &bc->addr));
515 offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 1);
516 return offReply;
517}
518
519static int dhcp_decode_discover(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size, int fDhcpDiscover, struct mbuf *m)
520{
521 BOOTPClient *bc;
522 struct in_addr daddr;
523 int offReply;
524
525 if (fDhcpDiscover)
526 {
527 bc = find_addr(pData, &daddr, bp->bp_hwaddr);
528 if (!bc)
529 {
530 bc = get_new_addr(pData, &daddr);
531 if (!bc)
532 {
533 LogRel(("NAT: DHCP no IP address left\n"));
534 Log(("no address left\n"));
535 return -1;
536 }
537 memcpy(bc->macaddr, bp->bp_hwaddr, 6);
538 }
539
540 bc->xid = bp->bp_xid;
541 LogRel(("NAT: DHCP offered IP address %R[IP4]\n", &bc->addr));
542 offReply = dhcp_send_offer(pData, bp, bc, m);
543 return offReply;
544 }
545 else
546 {
547 bc = find_addr(pData, &daddr, bp->bp_hwaddr);
548 if (!bc)
549 {
550 LogRel(("NAT: DHCP Inform was ignored no boot client was found\n"));
551 return -1;
552 }
553
554 LogRel(("NAT: DHCP offered IP address %R[IP4]\n", &bc->addr));
555 offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 0);
556 return offReply;
557 }
558
559 return -1;
560}
561
562static int dhcp_decode_release(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size)
563{
564 int rc = release_addr(pData, &bp->bp_ciaddr);
565 LogRel(("NAT: %s %R[IP4]\n",
566 RT_SUCCESS(rc) ? "DHCP released IP address" : "Ignored DHCP release for IP address",
567 &bp->bp_ciaddr));
568 return 0;
569}
570/**
571 * fields for discovering t
572 * Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
573 * DHCPINFORM DHCPRELEASE
574 * ----- ------------ ----------- -----------
575 * 'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
576 * 'htype' (From "Assigned Numbers" RFC)
577 * 'hlen' (Hardware address length in octets)
578 * 'hops' 0 0 0
579 * 'xid' selected by client 'xid' from server selected by
580 * DHCPOFFER message client
581 * 'secs' 0 or seconds since 0 or seconds since 0
582 * DHCP process started DHCP process started
583 * 'flags' Set 'BROADCAST' Set 'BROADCAST' 0
584 * flag if client flag if client
585 * requires broadcast requires broadcast
586 * reply reply
587 * 'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
588 * client's network address client's network
589 * network address (BOUND/RENEW/REBIND) address
590 * (DHCPINFORM) (DHCPRELEASE)
591 * 'yiaddr' 0 0 0
592 * 'siaddr' 0 0 0
593 * 'giaddr' 0 0 0
594 * 'chaddr' client's hardware client's hardware client's hardware
595 * address address address
596 * 'sname' options, if options, if (unused)
597 * indicated in indicated in
598 * 'sname/file' 'sname/file'
599 * option; otherwise option; otherwise
600 * unused unused
601 * 'file' options, if options, if (unused)
602 * indicated in indicated in
603 * 'sname/file' 'sname/file'
604 * option; otherwise option; otherwise
605 * unused unused
606 * 'options' options options (unused)
607 * Requested IP address MAY MUST (in MUST
608 * (DISCOVER) SELECTING or (DHCPDECLINE),
609 * MUST NOT INIT-REBOOT) MUST NOT
610 * (INFORM) MUST NOT (in (DHCPRELEASE)
611 * BOUND or
612 * RENEWING)
613 * IP address lease time MAY MAY MUST NOT
614 * (DISCOVER)
615 * MUST NOT
616 * (INFORM)
617 * Use 'file'/'sname' fields MAY MAY MAY
618 * DHCP message type DHCPDISCOVER/ DHCPREQUEST DHCPDECLINE/
619 * DHCPINFORM DHCPRELEASE
620 * Client identifier MAY MAY MAY
621 * Vendor class identifier MAY MAY MUST NOT
622 * Server identifier MUST NOT MUST (after MUST
623 * SELECTING)
624 * MUST NOT (after
625 * INIT-REBOOT,
626 * BOUND, RENEWING
627 * or REBINDING)
628 * Parameter request list MAY MAY MUST NOT
629 * Maximum message size MAY MAY MUST NOT
630 * Message SHOULD NOT SHOULD NOT SHOULD
631 * Site-specific MAY MAY MUST NOT
632 * All others MAY MAY MUST NOT
633 *
634 */
635static void dhcp_decode(PNATState pData, struct bootp_t *bp, const uint8_t *buf, int size)
636{
637 const uint8_t *p, *p_end;
638 int rc;
639 int pmsg_type;
640 struct in_addr req_ip;
641 int fDhcpDiscover = 0;
642 struct mbuf *m = NULL;
643
644 pmsg_type = 0;
645 p = buf;
646 p_end = buf + size;
647 if (size < 5)
648 return;
649
650 if (memcmp(p, rfc1533_cookie, 4) != 0)
651 return;
652
653 p = dhcp_find_option(bp->bp_vend, RFC2132_MSG_TYPE);
654 Assert(p);
655 if (p == NULL)
656 return;
657
658#ifndef VBOX_WITH_SLIRP_BSD_MBUF
659 if ((m = m_get(pData)) == NULL)
660#else
661 if ((m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR)) == NULL)
662#endif
663 {
664 LogRel(("NAT: can't alocate memory for response!\n"));
665 return;
666 }
667
668 switch (*(p+2))
669 {
670 case DHCPDISCOVER:
671 fDhcpDiscover = 1;
672 /**/
673 case DHCPINFORM:
674 rc = dhcp_decode_discover(pData, bp, buf, size, fDhcpDiscover, m);
675 if (rc > 0)
676 goto reply;
677 break;
678
679 case DHCPREQUEST:
680 rc = dhcp_decode_request(pData, bp, buf, size, m);
681 if (rc > 0)
682 goto reply;
683 break;
684
685 case DHCPRELEASE:
686 rc = dhcp_decode_release(pData, bp, buf, size);
687 /* no reply required */
688 break;
689
690 case DHCPDECLINE:
691 p = dhcp_find_option(&bp->bp_vend[0], RFC2132_REQ_ADDR);
692 req_ip.s_addr = *(uint32_t *)(p + 2);
693 rc = bootp_cache_lookup_ether_by_ip(pData, req_ip.s_addr, NULL);
694 if (RT_FAILURE(rc))
695 {
696 /* Not registered */
697 BOOTPClient *bc;
698 bc = bc_alloc_client(pData);
699 Assert(bc);
700 bc->addr.s_addr = req_ip.s_addr;
701 slirp_arp_who_has(pData, bc->addr.s_addr);
702 LogRel(("NAT: %R[IP4] has been already registered\n", &req_ip));
703 }
704 /* no response required */
705 break;
706
707 default:
708 AssertMsgFailed(("unsupported DHCP message type"));
709 }
710 Assert(m);
711 /*silently ignore*/
712 m_freem(pData, m);
713 return;
714
715reply:
716 bootp_reply(pData, m, rc, bp->bp_flags);
717 return;
718}
719
720static void bootp_reply(PNATState pData, struct mbuf *m, int offReply, uint16_t flags)
721{
722 struct sockaddr_in saddr, daddr;
723 struct bootp_t *rbp = NULL;
724 uint8_t *q = NULL;
725 int nack;
726 rbp = mtod(m, struct bootp_t *);
727 Assert((m));
728 Assert((rbp));
729 q = rbp->bp_vend;
730 nack = (q[6] == DHCPNAK);
731 q += offReply;
732
733#ifndef VBOX_WITH_NAT_SERVICE
734 saddr.sin_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
735#else
736 saddr.sin_addr.s_addr = pData->special_addr.s_addr;
737#endif
738
739 FILL_BOOTP_EXT(q, RFC2132_SRV_ID, 4, &saddr.sin_addr);
740
741 *q++ = RFC1533_END; /* end of message */
742
743#ifdef VBOX_WITH_SLIRP_BSD_MBUF
744 m->m_pkthdr.header = mtod(m, void *);
745#endif
746 m->m_len = sizeof(struct bootp_t)
747 - sizeof(struct ip)
748 - sizeof(struct udphdr);
749 m->m_data += sizeof(struct udphdr)
750 + sizeof(struct ip);
751 if ( (flags & RT_H2N_U16_C(DHCP_FLAGS_B))
752 || nack != 0)
753 daddr.sin_addr.s_addr = INADDR_BROADCAST;
754 else
755 daddr.sin_addr.s_addr = rbp->bp_yiaddr.s_addr; /*unicast requested by client*/
756 saddr.sin_port = RT_H2N_U16_C(BOOTP_SERVER);
757 daddr.sin_port = RT_H2N_U16_C(BOOTP_CLIENT);
758 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
759}
760
761void bootp_input(PNATState pData, struct mbuf *m)
762{
763 struct bootp_t *bp = mtod(m, struct bootp_t *);
764
765 if (bp->bp_op == BOOTP_REQUEST)
766 dhcp_decode(pData, bp, bp->bp_vend, DHCP_OPT_LEN);
767}
768
769int bootp_cache_lookup_ip_by_ether(PNATState pData,const uint8_t* ether, uint32_t *pip)
770{
771 int i;
772
773 if (!ether || !pip)
774 return VERR_INVALID_PARAMETER;
775
776 for (i = 0; i < NB_ADDR; i++)
777 {
778 if ( bootp_clients[i].allocated
779 && memcmp(bootp_clients[i].macaddr, ether, ETH_ALEN) == 0)
780 {
781 *pip = bootp_clients[i].addr.s_addr;
782 return VINF_SUCCESS;
783 }
784 }
785
786 *pip = INADDR_ANY;
787 return VERR_NOT_FOUND;
788}
789
790int bootp_cache_lookup_ether_by_ip(PNATState pData, uint32_t ip, uint8_t *ether)
791{
792 int i;
793 for (i = 0; i < NB_ADDR; i++)
794 {
795 if ( bootp_clients[i].allocated
796 && ip == bootp_clients[i].addr.s_addr)
797 {
798 if (ether != NULL)
799 memcpy(ether, bootp_clients[i].macaddr, ETH_ALEN);
800 return VINF_SUCCESS;
801 }
802 }
803
804 return VERR_NOT_FOUND;
805}
806
807/*
808 * Initialize dhcp server
809 * @returns 0 - if initialization is ok, non-zero otherwise
810 */
811int bootp_dhcp_init(PNATState pData)
812{
813 pData->pbootp_clients = RTMemAllocZ(sizeof(BOOTPClient) * NB_ADDR);
814 if (!pData->pbootp_clients)
815 return VERR_NO_MEMORY;
816
817 return VINF_SUCCESS;
818}
819
820int bootp_dhcp_fini(PNATState pData)
821{
822 if (pData->pbootp_clients != NULL)
823 RTMemFree(pData->pbootp_clients);
824
825 return VINF_SUCCESS;
826}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette