VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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

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