VirtualBox

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

Last change on this file since 57784 was 57784, checked in by vboxsync, 10 years ago

NAT: rewrite handling of port-forwarding.

The most interesting part is handling of wildcard guest address
(0.0.0.0) for which we are supposed to guess the real guest IP. For
TCP we delay the decision until new connection come and the we can use
the current guess for each new connection. For UDP things are
trickier. For now we set the current guess as the destination on
first incoming packet, but that doesn't handle changes of the guest
address or outgoing packets. This needs more thought.

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