VirtualBox

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

Last change on this file since 74910 was 73097, checked in by vboxsync, 7 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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