VirtualBox

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

Last change on this file since 86327 was 83591, checked in by vboxsync, 5 years ago

NAT: bugref:9703 - undo the functional change introduced in previous,
that code was obviosly never tested and causes fallout. The code now
should be equivalent to what it was before but with the extra checks.

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