VirtualBox

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

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

NAT: bugref:9703 - 90s called, fix declaration after code in previous.

  • 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 83581 2020-04-06 01:30:22Z 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 REBINDING:
576 LogRel(("NAT: REBINDING state isn't impemented\n"));
577 return -1;
578
579 case SELECTING:
580 LogRel(("NAT: SELECTING state isn't impemented\n"));
581 return -1;
582
583 case NONE:
584 return -1;
585
586 default:
587 break;
588 }
589
590 if (bc == NULL)
591 return -1;
592
593 LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
594 offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 1);
595 return offReply;
596}
597
598static int dhcp_decode_discover(PNATState pData, struct bootp_t *bp, int fDhcpDiscover, struct mbuf *m)
599{
600 BOOTPClient *bc;
601 struct in_addr daddr;
602 int offReply;
603
604 if (fDhcpDiscover)
605 {
606 bc = find_addr(pData, &daddr, bp->bp_hwaddr);
607 if (!bc)
608 {
609 bc = get_new_addr(pData, &daddr);
610 if (!bc)
611 {
612 LogRel(("NAT: DHCP no IP address left\n"));
613 Log(("no address left\n"));
614 return -1;
615 }
616 memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
617 }
618
619 bc->xid = bp->bp_xid;
620 LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
621 offReply = dhcp_send_offer(pData, bp, bc, m);
622 return offReply;
623 }
624
625 bc = find_addr(pData, &daddr, bp->bp_hwaddr);
626 if (!bc)
627 {
628 LogRel(("NAT: DHCP Inform was ignored no boot client was found\n"));
629 return -1;
630 }
631
632 LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
633 offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 0);
634 return offReply;
635}
636
637static int dhcp_decode_release(PNATState pData, struct bootp_t *bp)
638{
639 int rc = release_addr(pData, &bp->bp_ciaddr);
640 LogRel(("NAT: %s %RTnaipv4\n",
641 RT_SUCCESS(rc) ? "DHCP released IP address" : "Ignored DHCP release for IP address",
642 bp->bp_ciaddr.s_addr));
643 return 0;
644}
645
646/**
647 * fields for discovering t
648 * Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
649 * DHCPINFORM DHCPRELEASE
650 * ----- ------------ ----------- -----------
651 * 'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
652 * 'htype' (From "Assigned Numbers" RFC)
653 * 'hlen' (Hardware address length in octets)
654 * 'hops' 0 0 0
655 * 'xid' selected by client 'xid' from server selected by
656 * DHCPOFFER message client
657 * 'secs' 0 or seconds since 0 or seconds since 0
658 * DHCP process started DHCP process started
659 * 'flags' Set 'BROADCAST' Set 'BROADCAST' 0
660 * flag if client flag if client
661 * requires broadcast requires broadcast
662 * reply reply
663 * 'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
664 * client's network address client's network
665 * network address (BOUND/RENEW/REBIND) address
666 * (DHCPINFORM) (DHCPRELEASE)
667 * 'yiaddr' 0 0 0
668 * 'siaddr' 0 0 0
669 * 'giaddr' 0 0 0
670 * 'chaddr' client's hardware client's hardware client's hardware
671 * address address address
672 * 'sname' options, if options, if (unused)
673 * indicated in indicated in
674 * 'sname/file' 'sname/file'
675 * option; otherwise option; otherwise
676 * unused unused
677 * 'file' options, if options, if (unused)
678 * indicated in indicated in
679 * 'sname/file' 'sname/file'
680 * option; otherwise option; otherwise
681 * unused unused
682 * 'options' options options (unused)
683 * Requested IP address MAY MUST (in MUST
684 * (DISCOVER) SELECTING or (DHCPDECLINE),
685 * MUST NOT INIT-REBOOT) MUST NOT
686 * (INFORM) MUST NOT (in (DHCPRELEASE)
687 * BOUND or
688 * RENEWING)
689 * IP address lease time MAY MAY MUST NOT
690 * (DISCOVER)
691 * MUST NOT
692 * (INFORM)
693 * Use 'file'/'sname' fields MAY MAY MAY
694 * DHCP message type DHCPDISCOVER/ DHCPREQUEST DHCPDECLINE/
695 * DHCPINFORM DHCPRELEASE
696 * Client identifier MAY MAY MAY
697 * Vendor class identifier MAY MAY MUST NOT
698 * Server identifier MUST NOT MUST (after MUST
699 * SELECTING)
700 * MUST NOT (after
701 * INIT-REBOOT,
702 * BOUND, RENEWING
703 * or REBINDING)
704 * Parameter request list MAY MAY MUST NOT
705 * Maximum message size MAY MAY MUST NOT
706 * Message SHOULD NOT SHOULD NOT SHOULD
707 * Site-specific MAY MAY MUST NOT
708 * All others MAY MAY MUST NOT
709 *
710 */
711static void dhcp_decode(PNATState pData, struct bootp_t *bp, size_t vlen)
712{
713 const uint8_t *pu8RawDhcpObject;
714 int rc;
715 struct in_addr req_ip;
716 int fDhcpDiscover = 0;
717 uint8_t *parameter_list = NULL;
718 struct mbuf *m = NULL;
719
720 if (memcmp(bp->bp_vend, rfc1533_cookie, sizeof(rfc1533_cookie)) != 0)
721 return;
722
723 pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen, RFC2132_MSG_TYPE, 1);
724 if (pu8RawDhcpObject == NULL)
725 return;
726 if (pu8RawDhcpObject[1] != 1) /* option length */
727 return;
728
729 /**
730 * We're going update dns list at least once per DHCP transaction (!not on every operation
731 * within transaction), assuming that transaction can't be longer than 1 min.
732 *
733 * @note: if we have notification update (HAVE_NOTIFICATION_FOR_DNS_UPDATE)
734 * provided by host, we don't need implicitly re-initialize dns list.
735 *
736 * @note: NATState::fUseHostResolver became (r89055) the flag signalling that Slirp
737 * wasn't able to fetch fresh host DNS info and fall down to use host-resolver, on one
738 * of the previous attempts to proxy dns requests to Host's name-resolving API
739 *
740 * @note: Checking NATState::fUseHostResolver == true, we want to try restore behaviour initialy
741 * wanted by user ASAP (P here when host serialize its configuration in files parsed by Slirp).
742 */
743 if ( !HAVE_NOTIFICATION_FOR_DNS_UPDATE
744 && !pData->fUseHostResolverPermanent
745 && ( pData->dnsLastUpdate == 0
746 || curtime - pData->dnsLastUpdate > 60 * 1000 /* one minute */
747 || pData->fUseHostResolver))
748 {
749 uint8_t i;
750
751 parameter_list = dhcp_find_option(bp->bp_vend, vlen, RFC2132_PARAM_LIST, -1);
752 for (i = 0; parameter_list && i < parameter_list[1]; ++i)
753 {
754 if (parameter_list[2 + i] == RFC1533_DNS)
755 {
756 /* XXX: How differs it from host Suspend/Resume? */
757 slirpReleaseDnsSettings(pData);
758 slirpInitializeDnsSettings(pData);
759 pData->dnsLastUpdate = curtime;
760 break;
761 }
762 }
763 }
764
765 m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
766 if (!m)
767 {
768 LogRel(("NAT: Can't allocate memory for response!\n"));
769 return;
770 }
771
772 switch (*(pu8RawDhcpObject + 2))
773 {
774 case DHCPDISCOVER:
775 fDhcpDiscover = 1;
776 RT_FALL_THRU();
777 case DHCPINFORM:
778 rc = dhcp_decode_discover(pData, bp, fDhcpDiscover, m);
779 if (rc > 0)
780 goto reply;
781 break;
782
783 case DHCPREQUEST:
784 rc = dhcp_decode_request(pData, bp, vlen, m);
785 if (rc > 0)
786 goto reply;
787 break;
788
789 case DHCPRELEASE:
790 dhcp_decode_release(pData, bp);
791 /* no reply required */
792 break;
793
794 case DHCPDECLINE:
795 pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen,
796 RFC2132_REQ_ADDR, sizeof(struct in_addr));
797 if (pu8RawDhcpObject == NULL)
798 {
799 Log(("NAT: RFC2132_REQ_ADDR not found\n"));
800 break;
801 }
802
803 req_ip.s_addr = *(uint32_t *)(pu8RawDhcpObject + 2);
804 rc = bootp_cache_lookup_ether_by_ip(pData, req_ip.s_addr, NULL);
805 if (RT_FAILURE(rc))
806 {
807 /* Not registered */
808 BOOTPClient *bc;
809 bc = bc_alloc_client(pData);
810 Assert(bc);
811 if (!bc)
812 {
813 LogRel(("NAT: Can't allocate bootp client object\n"));
814 break;
815 }
816 bc->addr.s_addr = req_ip.s_addr;
817 slirp_arp_who_has(pData, bc->addr.s_addr);
818 LogRel(("NAT: %RTnaipv4 has been already registered\n", req_ip));
819 }
820 /* no response required */
821 break;
822
823 default:
824 /* unsupported DHCP message type */
825 break;
826 }
827 /* silently ignore */
828 m_freem(pData, m);
829 return;
830
831reply:
832 bootp_reply(pData, m, rc, bp->bp_flags);
833}
834
835static void bootp_reply(PNATState pData, struct mbuf *m, int offReply, uint16_t flags)
836{
837 struct sockaddr_in saddr, daddr;
838 struct bootp_t *rbp = NULL;
839 uint8_t *q = NULL;
840 int nack;
841 rbp = mtod(m, struct bootp_t *);
842 Assert((m));
843 Assert((rbp));
844 q = rbp->bp_vend;
845 nack = (q[6] == DHCPNAK);
846 q += offReply;
847
848 saddr.sin_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
849
850 FILL_BOOTP_EXT(q, RFC2132_SRV_ID, 4, &saddr.sin_addr);
851
852 *q++ = RFC1533_END; /* end of message */
853
854 m->m_pkthdr.header = mtod(m, void *);
855 m->m_len = sizeof(struct bootp_t)
856 - sizeof(struct ip)
857 - sizeof(struct udphdr);
858 m->m_data += sizeof(struct udphdr)
859 + sizeof(struct ip);
860 if ( (flags & RT_H2N_U16_C(DHCP_FLAGS_B))
861 || nack != 0)
862 daddr.sin_addr.s_addr = INADDR_BROADCAST;
863 else
864 daddr.sin_addr.s_addr = rbp->bp_yiaddr.s_addr; /*unicast requested by client*/
865 saddr.sin_port = RT_H2N_U16_C(BOOTP_SERVER);
866 daddr.sin_port = RT_H2N_U16_C(BOOTP_CLIENT);
867 udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
868}
869
870void bootp_input(PNATState pData, struct mbuf *m)
871{
872 struct bootp_t *bp = mtod(m, struct bootp_t *);
873 u_int mlen = m_length(m, NULL);
874 size_t vlen;
875
876 if (mlen < RT_UOFFSETOF(struct bootp_t, bp_vend) + sizeof(rfc1533_cookie))
877 {
878 LogRelMax(50, ("NAT: ignoring invalid BOOTP request (mlen %u too short)\n", mlen));
879 return;
880 }
881
882 if (bp->bp_op != BOOTP_REQUEST)
883 {
884 LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong opcode %u)\n", bp->bp_op));
885 return;
886 }
887
888 if (bp->bp_htype != RTNET_ARP_ETHER)
889 {
890 LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong HW type %u)\n", bp->bp_htype));
891 return;
892 }
893
894 if (bp->bp_hlen != ETH_ALEN)
895 {
896 LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong HW address length %u)\n", bp->bp_hlen));
897 return;
898 }
899
900 if (bp->bp_hops != 0)
901 {
902 LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong hop count %u)\n", bp->bp_hops));
903 return;
904 }
905
906 vlen = mlen - RT_UOFFSETOF(struct bootp_t, bp_vend);
907 dhcp_decode(pData, bp, vlen);
908}
909
910int bootp_cache_lookup_ip_by_ether(PNATState pData,const uint8_t* ether, uint32_t *pip)
911{
912 int i;
913
914 if (!ether || !pip)
915 return VERR_INVALID_PARAMETER;
916
917 for (i = 0; i < NB_ADDR; i++)
918 {
919 if ( bootp_clients[i].allocated
920 && memcmp(bootp_clients[i].macaddr, ether, ETH_ALEN) == 0)
921 {
922 *pip = bootp_clients[i].addr.s_addr;
923 return VINF_SUCCESS;
924 }
925 }
926
927 *pip = INADDR_ANY;
928 return VERR_NOT_FOUND;
929}
930
931int bootp_cache_lookup_ether_by_ip(PNATState pData, uint32_t ip, uint8_t *ether)
932{
933 int i;
934 for (i = 0; i < NB_ADDR; i++)
935 {
936 if ( bootp_clients[i].allocated
937 && ip == bootp_clients[i].addr.s_addr)
938 {
939 if (ether != NULL)
940 memcpy(ether, bootp_clients[i].macaddr, ETH_ALEN);
941 return VINF_SUCCESS;
942 }
943 }
944
945 return VERR_NOT_FOUND;
946}
947
948/*
949 * Initialize dhcp server
950 * @returns 0 - if initialization is ok, non-zero otherwise
951 */
952int bootp_dhcp_init(PNATState pData)
953{
954 pData->pbootp_clients = RTMemAllocZ(sizeof(BOOTPClient) * NB_ADDR);
955 if (!pData->pbootp_clients)
956 return VERR_NO_MEMORY;
957
958 return VINF_SUCCESS;
959}
960
961int bootp_dhcp_fini(PNATState pData)
962{
963 if (pData->pbootp_clients != NULL)
964 RTMemFree(pData->pbootp_clients);
965
966 return VINF_SUCCESS;
967}
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