VirtualBox

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

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

*: scm --update-copyright-year

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