VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvNAT.cpp

Last change on this file was 104389, checked in by vboxsync, 4 weeks ago

Devices/Neetwork/DrvNAT: linux.arm64 doesn't support 64KiB stacks for threads, so use 128KiB like the rest of the threads because that works, bugref:10391 [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.1 KB
Line 
1/* $Id: DrvNAT.cpp 104389 2024-04-20 19:15:12Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_NAT
33#define __STDC_LIMIT_MACROS
34#define __STDC_CONSTANT_MACROS
35#include "slirp/libslirp.h"
36extern "C" {
37#include "slirp/slirp_dns.h"
38}
39#include "slirp/ctl.h"
40
41#include <VBox/vmm/dbgf.h>
42#include <VBox/vmm/pdmdrv.h>
43#include <VBox/vmm/pdmnetifs.h>
44#include <VBox/vmm/pdmnetinline.h>
45
46#include <iprt/assert.h>
47#include <iprt/critsect.h>
48#include <iprt/cidr.h>
49#include <iprt/file.h>
50#include <iprt/mem.h>
51#include <iprt/pipe.h>
52#include <iprt/string.h>
53#include <iprt/stream.h>
54#include <iprt/uuid.h>
55
56#include "VBoxDD.h"
57
58#ifndef RT_OS_WINDOWS
59# include <unistd.h>
60# include <fcntl.h>
61# include <poll.h>
62# include <errno.h>
63#endif
64#ifdef RT_OS_FREEBSD
65# include <netinet/in.h>
66#endif
67#include <iprt/semaphore.h>
68#include <iprt/req.h>
69#ifdef RT_OS_DARWIN
70# include <SystemConfiguration/SystemConfiguration.h>
71# include <CoreFoundation/CoreFoundation.h>
72#endif
73
74#define COUNTERS_INIT
75#include "counters.h"
76
77
78/*********************************************************************************************************************************
79* Defined Constants And Macros *
80*********************************************************************************************************************************/
81
82#define DRVNAT_MAXFRAMESIZE (16 * 1024)
83
84/**
85 * @todo: This is a bad hack to prevent freezing the guest during high network
86 * activity. Windows host only. This needs to be fixed properly.
87 */
88#define VBOX_NAT_DELAY_HACK
89
90#define GET_EXTRADATA(pdrvins, node, name, rc, type, type_name, var) \
91do { \
92 (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var)); \
93 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
94 return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
95 (pdrvins)->iInstance); \
96} while (0)
97
98#define GET_ED_STRICT(pdrvins, node, name, rc, type, type_name, var) \
99do { \
100 (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var)); \
101 if (RT_FAILURE((rc))) \
102 return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
103 (pdrvins)->iInstance); \
104} while (0)
105
106#define GET_EXTRADATA_N(pdrvins, node, name, rc, type, type_name, var, var_size) \
107do { \
108 (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var), var_size); \
109 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
110 return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
111 (pdrvins)->iInstance); \
112} while (0)
113
114#define GET_BOOL(rc, pdrvins, node, name, var) \
115 GET_EXTRADATA(pdrvins, node, name, (rc), Bool, bolean, (var))
116#define GET_STRING(rc, pdrvins, node, name, var, var_size) \
117 GET_EXTRADATA_N(pdrvins, node, name, (rc), String, string, (var), (var_size))
118#define GET_STRING_ALLOC(rc, pdrvins, node, name, var) \
119 GET_EXTRADATA(pdrvins, node, name, (rc), StringAlloc, string, (var))
120#define GET_S32(rc, pdrvins, node, name, var) \
121 GET_EXTRADATA(pdrvins, node, name, (rc), S32, int, (var))
122#define GET_S32_STRICT(rc, pdrvins, node, name, var) \
123 GET_ED_STRICT(pdrvins, node, name, (rc), S32, int, (var))
124
125
126
127#define DO_GET_IP(rc, node, instance, status, x) \
128do { \
129 char sz##x[32]; \
130 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
131 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
132 (status) = inet_aton(sz ## x, &x); \
133} while (0)
134
135#define GETIP_DEF(rc, node, instance, x, def) \
136do \
137{ \
138 int status = 0; \
139 DO_GET_IP((rc), (node), (instance), status, x); \
140 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
141 x.s_addr = def; \
142} while (0)
143
144
145/*********************************************************************************************************************************
146* Structures and Typedefs *
147*********************************************************************************************************************************/
148/**
149 * NAT network transport driver instance data.
150 *
151 * @implements PDMINETWORKUP
152 */
153typedef struct DRVNAT
154{
155 /** The network interface. */
156 PDMINETWORKUP INetworkUp;
157 /** The network NAT Engine configureation. */
158 PDMINETWORKNATCONFIG INetworkNATCfg;
159 /** The port we're attached to. */
160 PPDMINETWORKDOWN pIAboveNet;
161 /** The network config of the port we're attached to. */
162 PPDMINETWORKCONFIG pIAboveConfig;
163 /** Pointer to the driver instance. */
164 PPDMDRVINS pDrvIns;
165 /** Link state */
166 PDMNETWORKLINKSTATE enmLinkState;
167 /** NAT state for this instance. */
168 PNATState pNATState;
169 /** TFTP directory prefix. */
170 char *pszTFTPPrefix;
171 /** Boot file name to provide in the DHCP server response. */
172 char *pszBootFile;
173 /** tftp server name to provide in the DHCP server response. */
174 char *pszNextServer;
175 /** Polling thread. */
176 PPDMTHREAD pSlirpThread;
177 /** Queue for NAT-thread-external events. */
178 RTREQQUEUE hSlirpReqQueue;
179 /** The guest IP for port-forwarding. */
180 uint32_t GuestIP;
181 /** Link state set when the VM is suspended. */
182 PDMNETWORKLINKSTATE enmLinkStateWant;
183
184#ifndef RT_OS_WINDOWS
185 /** The write end of the control pipe. */
186 RTPIPE hPipeWrite;
187 /** The read end of the control pipe. */
188 RTPIPE hPipeRead;
189# if HC_ARCH_BITS == 32
190 uint32_t u32Padding;
191# endif
192#else
193 /** for external notification */
194 HANDLE hWakeupEvent;
195#endif
196
197#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
198#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
199#include "counters.h"
200 /** thread delivering packets for receiving by the guest */
201 PPDMTHREAD pRecvThread;
202 /** thread delivering urg packets for receiving by the guest */
203 PPDMTHREAD pUrgRecvThread;
204 /** event to wakeup the guest receive thread */
205 RTSEMEVENT EventRecv;
206 /** event to wakeup the guest urgent receive thread */
207 RTSEMEVENT EventUrgRecv;
208 /** Receive Req queue (deliver packets to the guest) */
209 RTREQQUEUE hRecvReqQueue;
210 /** Receive Urgent Req queue (deliver packets to the guest). */
211 RTREQQUEUE hUrgRecvReqQueue;
212
213 /** makes access to device func RecvAvail and Recv atomical. */
214 RTCRITSECT DevAccessLock;
215 /** Number of in-flight urgent packets. */
216 volatile uint32_t cUrgPkts;
217 /** Number of in-flight regular packets. */
218 volatile uint32_t cPkts;
219
220 /** Transmit lock taken by BeginXmit and released by EndXmit. */
221 RTCRITSECT XmitLock;
222
223 /** Request queue for the async host resolver. */
224 RTREQQUEUE hHostResQueue;
225 /** Async host resolver thread. */
226 PPDMTHREAD pHostResThread;
227
228#ifdef RT_OS_DARWIN
229 /* Handle of the DNS watcher runloop source. */
230 CFRunLoopSourceRef hRunLoopSrcDnsWatcher;
231#endif
232} DRVNAT;
233AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
234/** Pointer to the NAT driver instance data. */
235typedef DRVNAT *PDRVNAT;
236
237
238/*********************************************************************************************************************************
239* Internal Functions *
240*********************************************************************************************************************************/
241static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho);
242DECLINLINE(void) drvNATUpdateDNS(PDRVNAT pThis, bool fFlapLink);
243static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis);
244
245
246/**
247 * @callback_method_impl{FNPDMTHREADDRV}
248 */
249static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
250{
251 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
252
253 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
254 return VINF_SUCCESS;
255
256 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
257 {
258 RTReqQueueProcess(pThis->hRecvReqQueue, 0);
259 if (ASMAtomicReadU32(&pThis->cPkts) == 0)
260 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
261 }
262 return VINF_SUCCESS;
263}
264
265
266/**
267 * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
268 */
269static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
270{
271 RT_NOREF(pThread);
272 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
273 int rc;
274 rc = RTSemEventSignal(pThis->EventRecv);
275
276 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
277 return VINF_SUCCESS;
278}
279
280
281/**
282 * @callback_method_impl{FNPDMTHREADDRV}
283 */
284static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
285{
286 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
287
288 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
289 return VINF_SUCCESS;
290
291 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
292 {
293 RTReqQueueProcess(pThis->hUrgRecvReqQueue, 0);
294 if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
295 {
296 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
297 AssertRC(rc);
298 }
299 }
300 return VINF_SUCCESS;
301}
302
303
304/**
305 * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
306 */
307static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
308{
309 RT_NOREF(pThread);
310 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
311 int rc = RTSemEventSignal(pThis->EventUrgRecv);
312 AssertRC(rc);
313
314 return VINF_SUCCESS;
315}
316
317
318static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
319{
320 int rc = RTCritSectEnter(&pThis->DevAccessLock);
321 AssertRC(rc);
322 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
323 if (RT_SUCCESS(rc))
324 {
325 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
326 AssertRC(rc);
327 }
328 else if ( rc != VERR_TIMEOUT
329 && rc != VERR_INTERRUPTED)
330 {
331 AssertRC(rc);
332 }
333
334 rc = RTCritSectLeave(&pThis->DevAccessLock);
335 AssertRC(rc);
336
337 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
338 if (ASMAtomicDecU32(&pThis->cUrgPkts) == 0)
339 {
340 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
341 drvNATNotifyNATThread(pThis, "drvNATUrgRecvWorker");
342 }
343}
344
345
346static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
347{
348 int rc;
349 STAM_PROFILE_START(&pThis->StatNATRecv, a);
350
351
352 while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
353 {
354 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
355 if ( RT_FAILURE(rc)
356 && ( rc == VERR_TIMEOUT
357 || rc == VERR_INTERRUPTED))
358 goto done_unlocked;
359 }
360
361 rc = RTCritSectEnter(&pThis->DevAccessLock);
362 AssertRC(rc);
363
364 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
365 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
366 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
367
368 if (RT_SUCCESS(rc))
369 {
370 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
371 AssertRC(rc);
372 }
373 else if ( rc != VERR_TIMEOUT
374 && rc != VERR_INTERRUPTED)
375 {
376 AssertRC(rc);
377 }
378
379 rc = RTCritSectLeave(&pThis->DevAccessLock);
380 AssertRC(rc);
381
382done_unlocked:
383 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
384 ASMAtomicDecU32(&pThis->cPkts);
385
386 drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
387
388 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
389}
390
391/**
392 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
393 *
394 * @param pThis Pointer to the NAT instance.
395 * @param pSgBuf The S/G buffer to free.
396 */
397static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
398{
399 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
400 pSgBuf->fFlags = 0;
401 if (pSgBuf->pvAllocator)
402 {
403 Assert(!pSgBuf->pvUser);
404 slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator, NULL);
405 pSgBuf->pvAllocator = NULL;
406 }
407 else if (pSgBuf->pvUser)
408 {
409 RTMemFree(pSgBuf->aSegs[0].pvSeg);
410 pSgBuf->aSegs[0].pvSeg = NULL;
411 RTMemFree(pSgBuf->pvUser);
412 pSgBuf->pvUser = NULL;
413 }
414 RTMemFree(pSgBuf);
415}
416
417/**
418 * Worker function for drvNATSend().
419 *
420 * @param pThis Pointer to the NAT instance.
421 * @param pSgBuf The scatter/gather buffer.
422 * @thread NAT
423 */
424static DECLCALLBACK(void) drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
425{
426#if 0 /* Assertion happens often to me after resuming a VM -- no time to investigate this now. */
427 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
428#endif
429 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
430 {
431 struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
432 if (m)
433 {
434 /*
435 * A normal frame.
436 */
437 pSgBuf->pvAllocator = NULL;
438 slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
439 }
440 else
441 {
442 /*
443 * GSO frame, need to segment it.
444 */
445 /** @todo Make the NAT engine grok large frames? Could be more efficient... */
446#if 0 /* this is for testing PDMNetGsoCarveSegmentQD. */
447 uint8_t abHdrScratch[256];
448#endif
449 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
450 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
451 /* Do not attempt to segment frames with invalid GSO parameters. */
452 if (PDMNetGsoIsValid(pGso, sizeof(*pGso), pSgBuf->cbUsed))
453 {
454 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
455 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
456 {
457 size_t cbSeg;
458 void *pvSeg;
459 m = slirp_ext_m_get(pThis->pNATState, pGso->cbHdrsTotal + pGso->cbMaxSeg, &pvSeg, &cbSeg);
460 if (!m)
461 break;
462
463#if 1
464 uint32_t cbPayload, cbHdrs;
465 uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
466 iSeg, cSegs, (uint8_t *)pvSeg, &cbHdrs, &cbPayload);
467 memcpy((uint8_t *)pvSeg + cbHdrs, pbFrame + offPayload, cbPayload);
468
469 slirp_input(pThis->pNATState, m, cbPayload + cbHdrs);
470#else
471 uint32_t cbSegFrame;
472 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
473 iSeg, cSegs, &cbSegFrame);
474 memcpy((uint8_t *)pvSeg, pvSegFrame, cbSegFrame);
475
476 slirp_input(pThis->pNATState, m, cbSegFrame);
477#endif
478 }
479 }
480 }
481 }
482 drvNATFreeSgBuf(pThis, pSgBuf);
483
484 /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf semantics. */
485}
486
487/**
488 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
489 */
490static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
491{
492 RT_NOREF(fOnWorkerThread);
493 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
494 int rc = RTCritSectTryEnter(&pThis->XmitLock);
495 if (RT_FAILURE(rc))
496 {
497 /** @todo Kick the worker thread when we have one... */
498 rc = VERR_TRY_AGAIN;
499 }
500 return rc;
501}
502
503/**
504 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
505 */
506static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
507 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
508{
509 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
510 Assert(RTCritSectIsOwner(&pThis->XmitLock));
511
512 /*
513 * Drop the incoming frame if the NAT thread isn't running.
514 */
515 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
516 {
517 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
518 return VERR_NET_NO_NETWORK;
519 }
520
521 /*
522 * Allocate a scatter/gather buffer and an mbuf.
523 */
524 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
525 if (!pSgBuf)
526 return VERR_NO_MEMORY;
527 if (!pGso)
528 {
529 /*
530 * Drop the frame if it is too big.
531 */
532 if (cbMin >= DRVNAT_MAXFRAMESIZE)
533 {
534 Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
535 cbMin));
536 RTMemFree(pSgBuf);
537 return VERR_INVALID_PARAMETER;
538 }
539
540 pSgBuf->pvUser = NULL;
541 pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
542 &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
543 if (!pSgBuf->pvAllocator)
544 {
545 RTMemFree(pSgBuf);
546 return VERR_TRY_AGAIN;
547 }
548 }
549 else
550 {
551 /*
552 * Drop the frame if its segment is too big.
553 */
554 if (pGso->cbHdrsTotal + pGso->cbMaxSeg >= DRVNAT_MAXFRAMESIZE)
555 {
556 Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
557 pGso->cbHdrsTotal + pGso->cbMaxSeg));
558 RTMemFree(pSgBuf);
559 return VERR_INVALID_PARAMETER;
560 }
561
562 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
563 pSgBuf->pvAllocator = NULL;
564 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
565 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
566 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
567 {
568 RTMemFree(pSgBuf->aSegs[0].pvSeg);
569 RTMemFree(pSgBuf->pvUser);
570 RTMemFree(pSgBuf);
571 return VERR_TRY_AGAIN;
572 }
573 }
574
575 /*
576 * Initialize the S/G buffer and return.
577 */
578 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
579 pSgBuf->cbUsed = 0;
580 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
581 pSgBuf->cSegs = 1;
582
583#if 0 /* poison */
584 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
585#endif
586 *ppSgBuf = pSgBuf;
587 return VINF_SUCCESS;
588}
589
590/**
591 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
592 */
593static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
594{
595 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
596 Assert(RTCritSectIsOwner(&pThis->XmitLock));
597 drvNATFreeSgBuf(pThis, pSgBuf);
598 return VINF_SUCCESS;
599}
600
601/**
602 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
603 */
604static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
605{
606 RT_NOREF(fOnWorkerThread);
607 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
608 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
609 Assert(RTCritSectIsOwner(&pThis->XmitLock));
610
611 int rc;
612 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
613 {
614 rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
615 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
616 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
617 if (RT_SUCCESS(rc))
618 {
619 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
620 return VINF_SUCCESS;
621 }
622
623 rc = VERR_NET_NO_BUFFER_SPACE;
624 }
625 else
626 rc = VERR_NET_DOWN;
627 drvNATFreeSgBuf(pThis, pSgBuf);
628 return rc;
629}
630
631/**
632 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
633 */
634static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
635{
636 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
637 RTCritSectLeave(&pThis->XmitLock);
638}
639
640/**
641 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
642 */
643static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
644{
645 RT_NOREF(pszWho);
646 int rc;
647#ifndef RT_OS_WINDOWS
648 /* kick poll() */
649 size_t cbIgnored;
650 rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
651#else
652 /* kick WSAWaitForMultipleEvents */
653 rc = WSASetEvent(pThis->hWakeupEvent);
654#endif
655 AssertRC(rc);
656}
657
658/**
659 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
660 */
661static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
662{
663 RT_NOREF(pInterface, fPromiscuous);
664 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
665 /* nothing to do */
666}
667
668/**
669 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
670 * @thread "NAT" thread.
671 */
672static DECLCALLBACK(void) drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
673{
674 pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
675 switch (enmLinkState)
676 {
677 case PDMNETWORKLINKSTATE_UP:
678 LogRel(("NAT: Link up\n"));
679 slirp_link_up(pThis->pNATState);
680 break;
681
682 case PDMNETWORKLINKSTATE_DOWN:
683 case PDMNETWORKLINKSTATE_DOWN_RESUME:
684 LogRel(("NAT: Link down\n"));
685 slirp_link_down(pThis->pNATState);
686 break;
687
688 default:
689 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
690 }
691}
692
693/**
694 * Notification on link status changes.
695 *
696 * @param pInterface Pointer to the interface structure containing the called function pointer.
697 * @param enmLinkState The new link state.
698 * @thread EMT
699 */
700static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
701{
702 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
703
704 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
705
706 /* Don't queue new requests if the NAT thread is not running (e.g. paused,
707 * stopping), otherwise we would deadlock. Memorize the change. */
708 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
709 {
710 pThis->enmLinkStateWant = enmLinkState;
711 return;
712 }
713
714 PRTREQ pReq;
715 int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
716 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
717 if (rc == VERR_TIMEOUT)
718 {
719 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
720 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
721 AssertRC(rc);
722 }
723 else
724 AssertRC(rc);
725 RTReqRelease(pReq);
726}
727
728static DECLCALLBACK(void) drvNATNotifyApplyPortForwardCommand(PDRVNAT pThis, bool fRemove,
729 bool fUdp, const char *pHostIp,
730 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
731{
732 struct in_addr guestIp, hostIp;
733
734 if ( pHostIp == NULL
735 || inet_aton(pHostIp, &hostIp) == 0)
736 hostIp.s_addr = INADDR_ANY;
737
738 if ( pGuestIp == NULL
739 || inet_aton(pGuestIp, &guestIp) == 0)
740 guestIp.s_addr = pThis->GuestIP;
741
742 if (fRemove)
743 slirp_remove_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
744 else
745 slirp_add_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
746}
747
748static DECLCALLBACK(int) drvNATNetworkNatConfigRedirect(PPDMINETWORKNATCONFIG pInterface, bool fRemove,
749 bool fUdp, const char *pHostIp, uint16_t u16HostPort,
750 const char *pGuestIp, uint16_t u16GuestPort)
751{
752 LogFlowFunc(("fRemove=%d, fUdp=%d, pHostIp=%s, u16HostPort=%u, pGuestIp=%s, u16GuestPort=%u\n",
753 RT_BOOL(fRemove), RT_BOOL(fUdp), pHostIp, u16HostPort, pGuestIp, u16GuestPort));
754 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
755 /* Execute the command directly if the VM is not running. */
756 int rc;
757 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
758 {
759 drvNATNotifyApplyPortForwardCommand(pThis, fRemove, fUdp, pHostIp,
760 u16HostPort, pGuestIp,u16GuestPort);
761 rc = VINF_SUCCESS;
762 }
763 else
764 {
765 PRTREQ pReq;
766 rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
767 (PFNRT)drvNATNotifyApplyPortForwardCommand, 7, pThis, fRemove,
768 fUdp, pHostIp, u16HostPort, pGuestIp, u16GuestPort);
769 if (rc == VERR_TIMEOUT)
770 {
771 drvNATNotifyNATThread(pThis, "drvNATNetworkNatConfigRedirect");
772 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
773 AssertRC(rc);
774 }
775 else
776 AssertRC(rc);
777
778 RTReqRelease(pReq);
779 }
780 return rc;
781}
782
783/**
784 * NAT thread handling the slirp stuff.
785 *
786 * The slirp implementation is single-threaded so we execute this enginre in a
787 * dedicated thread. We take care that this thread does not become the
788 * bottleneck: If the guest wants to send, a request is enqueued into the
789 * hSlirpReqQueue and handled asynchronously by this thread. If this thread
790 * wants to deliver packets to the guest, it enqueues a request into
791 * hRecvReqQueue which is later handled by the Recv thread.
792 */
793static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
794{
795 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
796 int nFDs = -1;
797#ifdef RT_OS_WINDOWS
798 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
799 unsigned int cBreak = 0;
800#else /* RT_OS_WINDOWS */
801 unsigned int cPollNegRet = 0;
802#endif /* !RT_OS_WINDOWS */
803
804 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
805
806 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
807 return VINF_SUCCESS;
808
809 if (pThis->enmLinkStateWant != pThis->enmLinkState)
810 drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
811
812 /*
813 * Polling loop.
814 */
815 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
816 {
817 /*
818 * To prevent concurrent execution of sending/receiving threads
819 */
820#ifndef RT_OS_WINDOWS
821 nFDs = slirp_get_nsock(pThis->pNATState);
822 /* allocation for all sockets + Management pipe */
823 struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
824 if (polls == NULL)
825 return VERR_NO_MEMORY;
826
827 /* don't pass the management pipe */
828 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
829
830 polls[0].fd = RTPipeToNative(pThis->hPipeRead);
831 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
832 polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
833 polls[0].revents = 0;
834
835 int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
836 if (cChangedFDs < 0)
837 {
838 if (errno == EINTR)
839 {
840 Log2(("NAT: signal was caught while sleep on poll\n"));
841 /* No error, just process all outstanding requests but don't wait */
842 cChangedFDs = 0;
843 }
844 else if (cPollNegRet++ > 128)
845 {
846 LogRel(("NAT: Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
847 cPollNegRet = 0;
848 }
849 }
850
851 if (cChangedFDs >= 0)
852 {
853 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
854 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
855 {
856 /* drain the pipe
857 *
858 * Note! drvNATSend decoupled so we don't know how many times
859 * device's thread sends before we've entered multiplex,
860 * so to avoid false alarm drain pipe here to the very end
861 *
862 * @todo: Probably we should counter drvNATSend to count how
863 * deep pipe has been filed before drain.
864 *
865 */
866 /** @todo XXX: Make it reading exactly we need to drain the
867 * pipe.*/
868 char ch;
869 size_t cbRead;
870 RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
871 }
872 }
873 /* process _all_ outstanding requests but don't wait */
874 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
875 RTMemFree(polls);
876
877#else /* RT_OS_WINDOWS */
878 nFDs = -1;
879 slirp_select_fill(pThis->pNATState, &nFDs);
880 DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
881 slirp_get_timeout_ms(pThis->pNATState),
882 /* :fAlertable */ TRUE);
883 AssertCompile(WSA_WAIT_EVENT_0 == 0);
884 if ( (/*dwEvent < WSA_WAIT_EVENT_0 ||*/ dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
885 && dwEvent != WSA_WAIT_TIMEOUT && dwEvent != WSA_WAIT_IO_COMPLETION)
886 {
887 int error = WSAGetLastError();
888 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
889 RTAssertPanic();
890 }
891
892 if (dwEvent == WSA_WAIT_TIMEOUT)
893 {
894 /* only check for slow/fast timers */
895 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true);
896 continue;
897 }
898 /* poll the sockets in any case */
899 Log2(("%s: poll\n", __FUNCTION__));
900 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false);
901 /* process _all_ outstanding requests but don't wait */
902 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
903# ifdef VBOX_NAT_DELAY_HACK
904 if (cBreak++ > 128)
905 {
906 cBreak = 0;
907 RTThreadSleep(2);
908 }
909# endif
910#endif /* RT_OS_WINDOWS */
911 }
912
913 return VINF_SUCCESS;
914}
915
916
917/**
918 * Unblock the send thread so it can respond to a state change.
919 *
920 * @returns VBox status code.
921 * @param pDevIns The pcnet device instance.
922 * @param pThread The send thread.
923 */
924static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
925{
926 RT_NOREF(pThread);
927 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
928
929 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
930 return VINF_SUCCESS;
931}
932
933
934static DECLCALLBACK(int) drvNATHostResThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
935{
936 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
937
938 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
939 return VINF_SUCCESS;
940
941 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
942 {
943 RTReqQueueProcess(pThis->hHostResQueue, RT_INDEFINITE_WAIT);
944 }
945
946 return VINF_SUCCESS;
947}
948
949
950static DECLCALLBACK(int) drvNATReqQueueInterrupt()
951{
952 /*
953 * RTReqQueueProcess loops until request returns a warning or info
954 * status code (other than VINF_SUCCESS).
955 */
956 return VINF_INTERRUPTED;
957}
958
959
960static DECLCALLBACK(int) drvNATHostResWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
961{
962 RT_NOREF(pThread);
963 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
964 Assert(pThis != NULL);
965
966 int rc;
967 rc = RTReqQueueCallEx(pThis->hHostResQueue, NULL /*ppReq*/, 0 /*cMillies*/,
968 RTREQFLAGS_IPRT_STATUS | RTREQFLAGS_NO_WAIT,
969 (PFNRT)drvNATReqQueueInterrupt, 0);
970 return rc;
971}
972
973
974#if 0 /* unused */
975/**
976 * Function called by slirp to check if it's possible to feed incoming data to the network port.
977 * @returns 1 if possible.
978 * @returns 0 if not possible.
979 */
980int slirp_can_output(void *pvUser)
981{
982 RT_NOREF(pvUser);
983 return 1;
984}
985
986static void slirp_push_recv_thread(void *pvUser)
987{
988 PDRVNAT pThis = (PDRVNAT)pvUser;
989 Assert(pThis);
990 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
991}
992#endif
993
994void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
995{
996 PDRVNAT pThis = (PDRVNAT)pvUser;
997 Assert(pThis);
998
999 /* don't queue new requests when the NAT thread is about to stop */
1000 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
1001 return;
1002
1003 ASMAtomicIncU32(&pThis->cUrgPkts);
1004 int rc = RTReqQueueCallEx(pThis->hUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1005 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
1006 AssertRC(rc);
1007 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
1008}
1009
1010/**
1011 * Function called by slirp to wake up device after VERR_TRY_AGAIN
1012 */
1013void slirp_output_pending(void *pvUser)
1014{
1015 PDRVNAT pThis = (PDRVNAT)pvUser;
1016 Assert(pThis);
1017 LogFlowFuncEnter();
1018 pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
1019 LogFlowFuncLeave();
1020}
1021
1022/**
1023 * Function called by slirp to feed incoming data to the NIC.
1024 */
1025void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
1026{
1027 PDRVNAT pThis = (PDRVNAT)pvUser;
1028 Assert(pThis);
1029
1030 LogFlow(("slirp_output BEGIN %p %d\n", pu8Buf, cb));
1031 Log6(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
1032
1033 /* don't queue new requests when the NAT thread is about to stop */
1034 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
1035 return;
1036
1037 ASMAtomicIncU32(&pThis->cPkts);
1038 int rc = RTReqQueueCallEx(pThis->hRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1039 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
1040 AssertRC(rc);
1041 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
1042 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
1043 LogFlowFuncLeave();
1044}
1045
1046
1047/*
1048 * Call a function on the slirp thread.
1049 */
1050int slirp_call(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
1051 unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
1052{
1053 PDRVNAT pThis = (PDRVNAT)pvUser;
1054 Assert(pThis);
1055
1056 int rc;
1057
1058 va_list va;
1059 va_start(va, cArgs);
1060
1061 rc = RTReqQueueCallV(pThis->hSlirpReqQueue, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
1062
1063 va_end(va);
1064
1065 if (RT_SUCCESS(rc))
1066 drvNATNotifyNATThread(pThis, "slirp_vcall");
1067
1068 return rc;
1069}
1070
1071
1072/*
1073 * Call a function on the host resolver thread.
1074 */
1075int slirp_call_hostres(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
1076 unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
1077{
1078 PDRVNAT pThis = (PDRVNAT)pvUser;
1079 Assert(pThis);
1080
1081 int rc;
1082
1083 AssertReturn((pThis->hHostResQueue != NIL_RTREQQUEUE), VERR_INVALID_STATE);
1084 AssertReturn((pThis->pHostResThread != NULL), VERR_INVALID_STATE);
1085
1086 va_list va;
1087 va_start(va, cArgs);
1088
1089 rc = RTReqQueueCallV(pThis->hHostResQueue, ppReq, cMillies, fFlags,
1090 pfnFunction, cArgs, va);
1091
1092 va_end(va);
1093 return rc;
1094}
1095
1096
1097#if HAVE_NOTIFICATION_FOR_DNS_UPDATE && !defined(RT_OS_DARWIN)
1098/**
1099 * @interface_method_impl{PDMINETWORKNATCONFIG,pfnNotifyDnsChanged}
1100 *
1101 * We are notified that host's resolver configuration has changed. In
1102 * the current setup we don't get any details and just reread that
1103 * information ourselves.
1104 */
1105static DECLCALLBACK(void) drvNATNotifyDnsChanged(PPDMINETWORKNATCONFIG pInterface)
1106{
1107 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
1108 drvNATUpdateDNS(pThis, /* fFlapLink */ true);
1109}
1110#endif
1111
1112#ifdef RT_OS_DARWIN
1113/**
1114 * Callback for the SystemConfiguration framework to notify us whenever the DNS
1115 * server changes.
1116 *
1117 * @param hDynStor The DynamicStore handle.
1118 * @param hChangedKey Array of changed keys we watch for.
1119 * @param pvUser Opaque user data (NAT driver instance).
1120 */
1121static DECLCALLBACK(void) drvNatDnsChanged(SCDynamicStoreRef hDynStor, CFArrayRef hChangedKeys, void *pvUser)
1122{
1123 PDRVNAT pThis = (PDRVNAT)pvUser;
1124
1125 Log2(("NAT: System configuration has changed\n"));
1126
1127 /* Check if any of parameters we are interested in were actually changed. If the size
1128 * of hChangedKeys is 0, it means that SCDynamicStore has been restarted. */
1129 if (hChangedKeys && CFArrayGetCount(hChangedKeys) > 0)
1130 {
1131 /* Look to the updated parameters in particular. */
1132 CFStringRef pDNSKey = CFSTR("State:/Network/Global/DNS");
1133
1134 if (CFArrayContainsValue(hChangedKeys, CFRangeMake(0, CFArrayGetCount(hChangedKeys)), pDNSKey))
1135 {
1136 LogRel(("NAT: DNS servers changed, triggering reconnect\n"));
1137#if 0
1138 CFDictionaryRef hDnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(hDynStor, pDNSKey);
1139 if (hDnsDict)
1140 {
1141 CFArrayRef hArrAddresses = (CFArrayRef)CFDictionaryGetValue(hDnsDict, kSCPropNetDNSServerAddresses);
1142 if (hArrAddresses && CFArrayGetCount(hArrAddresses) > 0)
1143 {
1144 /* Dump DNS servers list. */
1145 for (int i = 0; i < CFArrayGetCount(hArrAddresses); i++)
1146 {
1147 CFStringRef pDNSAddrStr = (CFStringRef)CFArrayGetValueAtIndex(hArrAddresses, i);
1148 const char *pszDNSAddr = pDNSAddrStr ? CFStringGetCStringPtr(pDNSAddrStr, CFStringGetSystemEncoding()) : NULL;
1149 LogRel(("NAT: New DNS server#%d: %s\n", i, pszDNSAddr ? pszDNSAddr : "None"));
1150 }
1151 }
1152 else
1153 LogRel(("NAT: DNS server list is empty (1)\n"));
1154
1155 CFRelease(hDnsDict);
1156 }
1157 else
1158 LogRel(("NAT: DNS server list is empty (2)\n"));
1159#else
1160 RT_NOREF(hDynStor);
1161#endif
1162 drvNATUpdateDNS(pThis, /* fFlapLink */ true);
1163 }
1164 else
1165 Log2(("NAT: No DNS changes detected\n"));
1166 }
1167 else
1168 Log2(("NAT: SCDynamicStore has been restarted\n"));
1169}
1170#endif
1171
1172/**
1173 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1174 */
1175static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1176{
1177 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1178 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1179
1180 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1181 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
1182 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKNATCONFIG, &pThis->INetworkNATCfg);
1183 return NULL;
1184}
1185
1186
1187/**
1188 * Get the MAC address into the slirp stack.
1189 *
1190 * Called by drvNATLoadDone and drvNATPowerOn.
1191 */
1192static void drvNATSetMac(PDRVNAT pThis)
1193{
1194#if 0 /* XXX: do we still need this for anything? */
1195 if (pThis->pIAboveConfig)
1196 {
1197 RTMAC Mac;
1198 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
1199 }
1200#else
1201 RT_NOREF(pThis);
1202#endif
1203}
1204
1205
1206/**
1207 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
1208 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
1209 * (usually done during guest boot).
1210 */
1211static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
1212{
1213 RT_NOREF(pSSM);
1214 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1215 drvNATSetMac(pThis);
1216 return VINF_SUCCESS;
1217}
1218
1219
1220/**
1221 * Some guests might not use DHCP to retrieve an IP but use a static IP.
1222 */
1223static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
1224{
1225 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1226 drvNATSetMac(pThis);
1227}
1228
1229
1230/**
1231 * @interface_method_impl{PDMDRVREG,pfnResume}
1232 */
1233static DECLCALLBACK(void) drvNATResume(PPDMDRVINS pDrvIns)
1234{
1235 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1236 VMRESUMEREASON enmReason = PDMDrvHlpVMGetResumeReason(pDrvIns);
1237
1238 switch (enmReason)
1239 {
1240 case VMRESUMEREASON_HOST_RESUME:
1241 bool fFlapLink;
1242#if HAVE_NOTIFICATION_FOR_DNS_UPDATE
1243 /* let event handler do it if necessary */
1244 fFlapLink = false;
1245#else
1246 /* XXX: when in doubt, use brute force */
1247 fFlapLink = true;
1248#endif
1249 drvNATUpdateDNS(pThis, fFlapLink);
1250 return;
1251 default: /* Ignore every other resume reason. */
1252 /* do nothing */
1253 return;
1254 }
1255}
1256
1257
1258static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis)
1259{
1260 slirpReleaseDnsSettings(pThis->pNATState);
1261 slirpInitializeDnsSettings(pThis->pNATState);
1262 return VINF_SUCCESS;
1263}
1264
1265/**
1266 * This function at this stage could be called from two places, but both from non-NAT thread,
1267 * - drvNATResume (EMT?)
1268 * - drvNatDnsChanged (darwin, GUI or main) "listener"
1269 * When Main's interface IHost will support host network configuration change event on every host,
1270 * we won't call it from drvNATResume, but from listener of Main event in the similar way it done
1271 * for port-forwarding, and it wan't be on GUI/main thread, but on EMT thread only.
1272 *
1273 * Thread here is important, because we need to change DNS server list and domain name (+ perhaps,
1274 * search string) at runtime (VBOX_NAT_ENFORCE_INTERNAL_DNS_UPDATE), we can do it safely on NAT thread,
1275 * so with changing other variables (place where we handle update) the main mechanism of update
1276 * _won't_ be changed, the only thing will change is drop of fFlapLink parameter.
1277 */
1278DECLINLINE(void) drvNATUpdateDNS(PDRVNAT pThis, bool fFlapLink)
1279{
1280 int strategy = slirp_host_network_configuration_change_strategy_selector(pThis->pNATState);
1281 switch (strategy)
1282 {
1283 case VBOX_NAT_DNS_DNSPROXY:
1284 {
1285 /**
1286 * XXX: Here or in _strategy_selector we should deal with network change
1287 * in "network change" scenario domain name change we have to update guest lease
1288 * forcibly.
1289 * Note at that built-in dhcp also updates DNS information on NAT thread.
1290 */
1291 /**
1292 * It's unsafe to to do it directly on non-NAT thread
1293 * so we schedule the worker and kick the NAT thread.
1294 */
1295 int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
1296 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1297 (PFNRT)drvNATReinitializeHostNameResolving, 1, pThis);
1298 if (RT_SUCCESS(rc))
1299 drvNATNotifyNATThread(pThis, "drvNATUpdateDNS");
1300
1301 return;
1302 }
1303
1304 case VBOX_NAT_DNS_EXTERNAL:
1305 /*
1306 * Host resumed from a suspend and the network might have changed.
1307 * Disconnect the guest from the network temporarily to let it pick up the changes.
1308 */
1309 if (fFlapLink)
1310 pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig,
1311 PDMNETWORKLINKSTATE_DOWN_RESUME);
1312 return;
1313
1314 case VBOX_NAT_DNS_HOSTRESOLVER:
1315 default:
1316 return;
1317 }
1318}
1319
1320
1321/**
1322 * Info handler.
1323 */
1324static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1325{
1326 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1327 slirp_info(pThis->pNATState, pHlp, pszArgs);
1328}
1329
1330#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1331static int drvNATConstructDNSMappings(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pMappingsCfg)
1332{
1333 PPDMDRVINS pDrvIns = pThis->pDrvIns;
1334 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1335
1336 RT_NOREF(iInstance);
1337 int rc = VINF_SUCCESS;
1338 LogFlowFunc(("ENTER: iInstance:%d\n", iInstance));
1339 for (PCFGMNODE pNode = pHlp->pfnCFGMGetFirstChild(pMappingsCfg); pNode; pNode = pHlp->pfnCFGMGetNextChild(pNode))
1340 {
1341 if (!pHlp->pfnCFGMAreValuesValid(pNode, "HostName\0HostNamePattern\0HostIP\0"))
1342 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1343 N_("Unknown configuration in dns mapping"));
1344 char szHostNameOrPattern[255];
1345 bool fPattern = false;
1346 RT_ZERO(szHostNameOrPattern);
1347 GET_STRING(rc, pDrvIns, pNode, "HostName", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
1348 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1349 {
1350 GET_STRING(rc, pDrvIns, pNode, "HostNamePattern", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
1351 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1352 {
1353 char szNodeName[225];
1354 RT_ZERO(szNodeName);
1355 pHlp->pfnCFGMGetName(pNode, szNodeName, sizeof(szNodeName));
1356 LogRel(("NAT: Neither 'HostName' nor 'HostNamePattern' is specified for mapping %s\n", szNodeName));
1357 continue;
1358 }
1359 fPattern = true;
1360 }
1361 struct in_addr HostIP;
1362 RT_ZERO(HostIP);
1363 GETIP_DEF(rc, pDrvIns, pNode, HostIP, INADDR_ANY);
1364 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1365 {
1366 LogRel(("NAT: DNS mapping %s is ignored (address not pointed)\n", szHostNameOrPattern));
1367 continue;
1368 }
1369 slirp_add_host_resolver_mapping(pThis->pNATState, szHostNameOrPattern, fPattern, HostIP.s_addr);
1370 }
1371 LogFlowFunc(("LEAVE: %Rrc\n", rc));
1372 return rc;
1373}
1374#endif /* !VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER */
1375
1376
1377/**
1378 * Sets up the redirectors.
1379 *
1380 * @returns VBox status code.
1381 * @param pCfg The configuration handle.
1382 */
1383static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, PRTNETADDRIPV4 pNetwork)
1384{
1385 PPDMDRVINS pDrvIns = pThis->pDrvIns;
1386 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1387
1388 RT_NOREF(pNetwork); /** @todo figure why pNetwork isn't used */
1389
1390 PCFGMNODE pPFTree = pHlp->pfnCFGMGetChild(pCfg, "PortForwarding");
1391 if (pPFTree == NULL)
1392 return VINF_SUCCESS;
1393
1394 /*
1395 * Enumerate redirections.
1396 */
1397 for (PCFGMNODE pNode = pHlp->pfnCFGMGetFirstChild(pPFTree); pNode; pNode = pHlp->pfnCFGMGetNextChild(pNode))
1398 {
1399 /*
1400 * Validate the port forwarding config.
1401 */
1402 if (!pHlp->pfnCFGMAreValuesValid(pNode, "Name\0Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
1403 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1404 N_("Unknown configuration in port forwarding"));
1405
1406 /* protocol type */
1407 bool fUDP;
1408 char szProtocol[32];
1409 int rc;
1410 GET_STRING(rc, pDrvIns, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
1411 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1412 {
1413 fUDP = false;
1414 GET_BOOL(rc, pDrvIns, pNode, "UDP", fUDP);
1415 }
1416 else if (RT_SUCCESS(rc))
1417 {
1418 if (!RTStrICmp(szProtocol, "TCP"))
1419 fUDP = false;
1420 else if (!RTStrICmp(szProtocol, "UDP"))
1421 fUDP = true;
1422 else
1423 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1424 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
1425 iInstance, szProtocol);
1426 }
1427 else
1428 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1429 N_("NAT#%d: configuration query for \"Protocol\" failed"),
1430 iInstance);
1431 /* host port */
1432 int32_t iHostPort;
1433 GET_S32_STRICT(rc, pDrvIns, pNode, "HostPort", iHostPort);
1434
1435 /* guest port */
1436 int32_t iGuestPort;
1437 GET_S32_STRICT(rc, pDrvIns, pNode, "GuestPort", iGuestPort);
1438
1439 /* host address ("BindIP" name is rather unfortunate given "HostPort" to go with it) */
1440 struct in_addr BindIP;
1441 RT_ZERO(BindIP);
1442 GETIP_DEF(rc, pDrvIns, pNode, BindIP, INADDR_ANY);
1443
1444 /* guest address */
1445 struct in_addr GuestIP;
1446 RT_ZERO(GuestIP);
1447 GETIP_DEF(rc, pDrvIns, pNode, GuestIP, INADDR_ANY);
1448
1449 /*
1450 * Call slirp about it.
1451 */
1452 if (slirp_add_redirect(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort) < 0)
1453 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1454 N_("NAT#%d: configuration error: failed to set up "
1455 "redirection of %d to %d. Probably a conflict with "
1456 "existing services or other rules"), iInstance, iHostPort,
1457 iGuestPort);
1458 } /* for each redir rule */
1459
1460 return VINF_SUCCESS;
1461}
1462
1463
1464/**
1465 * Destruct a driver instance.
1466 *
1467 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1468 * resources can be freed correctly.
1469 *
1470 * @param pDrvIns The driver instance data.
1471 */
1472static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1473{
1474 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1475 LogFlow(("drvNATDestruct:\n"));
1476 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1477
1478 if (pThis->pNATState)
1479 {
1480 slirp_term(pThis->pNATState);
1481 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1482#ifdef VBOX_WITH_STATISTICS
1483# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1484# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1485# include "counters.h"
1486#endif
1487 pThis->pNATState = NULL;
1488 }
1489
1490 RTReqQueueDestroy(pThis->hHostResQueue);
1491 pThis->hHostResQueue = NIL_RTREQQUEUE;
1492
1493 RTReqQueueDestroy(pThis->hSlirpReqQueue);
1494 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1495
1496 RTReqQueueDestroy(pThis->hUrgRecvReqQueue);
1497 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1498
1499 RTReqQueueDestroy(pThis->hRecvReqQueue);
1500 pThis->hRecvReqQueue = NIL_RTREQQUEUE;
1501
1502 RTSemEventDestroy(pThis->EventRecv);
1503 pThis->EventRecv = NIL_RTSEMEVENT;
1504
1505 RTSemEventDestroy(pThis->EventUrgRecv);
1506 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1507
1508 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1509 RTCritSectDelete(&pThis->DevAccessLock);
1510
1511 if (RTCritSectIsInitialized(&pThis->XmitLock))
1512 RTCritSectDelete(&pThis->XmitLock);
1513
1514#ifndef RT_OS_WINDOWS
1515 RTPipeClose(pThis->hPipeRead);
1516 RTPipeClose(pThis->hPipeWrite);
1517#endif
1518
1519#ifdef RT_OS_DARWIN
1520 /* Cleanup the DNS watcher. */
1521 if (pThis->hRunLoopSrcDnsWatcher != NULL)
1522 {
1523 CFRunLoopRef hRunLoopMain = CFRunLoopGetMain();
1524 CFRetain(hRunLoopMain);
1525 CFRunLoopRemoveSource(hRunLoopMain, pThis->hRunLoopSrcDnsWatcher, kCFRunLoopCommonModes);
1526 CFRelease(hRunLoopMain);
1527 CFRelease(pThis->hRunLoopSrcDnsWatcher);
1528 pThis->hRunLoopSrcDnsWatcher = NULL;
1529 }
1530#endif
1531}
1532
1533
1534/**
1535 * Construct a NAT network transport driver instance.
1536 *
1537 * @copydoc FNPDMDRVCONSTRUCT
1538 */
1539static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1540{
1541 RT_NOREF(fFlags);
1542 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1543 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1544 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1545
1546 LogFlow(("drvNATConstruct:\n"));
1547
1548 /*
1549 * Init the static parts.
1550 */
1551 pThis->pDrvIns = pDrvIns;
1552 pThis->pNATState = NULL;
1553 pThis->pszTFTPPrefix = NULL;
1554 pThis->pszBootFile = NULL;
1555 pThis->pszNextServer = NULL;
1556 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1557 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1558 pThis->hHostResQueue = NIL_RTREQQUEUE;
1559 pThis->EventRecv = NIL_RTSEMEVENT;
1560 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1561#ifdef RT_OS_DARWIN
1562 pThis->hRunLoopSrcDnsWatcher = NULL;
1563#endif
1564
1565 /* IBase */
1566 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1567
1568 /* INetwork */
1569 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1570 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1571 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1572 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1573 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1574 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1575 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1576
1577 /* NAT engine configuration */
1578 pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfigRedirect;
1579#if HAVE_NOTIFICATION_FOR_DNS_UPDATE && !defined(RT_OS_DARWIN)
1580 /*
1581 * On OS X we stick to the old OS X specific notifications for
1582 * now. Elsewhere use IHostNameResolutionConfigurationChangeEvent
1583 * by enbaling HAVE_NOTIFICATION_FOR_DNS_UPDATE in libslirp.h.
1584 * This code is still in a bit of flux and is implemented and
1585 * enabled in steps to simplify more conservative backporting.
1586 */
1587 pThis->INetworkNATCfg.pfnNotifyDnsChanged = drvNATNotifyDnsChanged;
1588#else
1589 pThis->INetworkNATCfg.pfnNotifyDnsChanged = NULL;
1590#endif
1591
1592 /*
1593 * Validate the config.
1594 */
1595 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
1596 "PassDomain"
1597 "|TFTPPrefix"
1598 "|BootFile"
1599 "|Network"
1600 "|NextServer"
1601 "|DNSProxy"
1602 "|BindIP"
1603 "|UseHostResolver"
1604 "|SlirpMTU"
1605 "|AliasMode"
1606 "|SockRcv"
1607 "|SockSnd"
1608 "|TcpRcv"
1609 "|TcpSnd"
1610 "|ICMPCacheLimit"
1611 "|SoMaxConnection"
1612 "|LocalhostReachable"
1613//#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1614 "|HostResolverMappings"
1615//#endif
1616 , "PortForwarding");
1617
1618 /*
1619 * Get the configuration settings.
1620 */
1621 int rc;
1622 bool fPassDomain = true;
1623 GET_BOOL(rc, pDrvIns, pCfg, "PassDomain", fPassDomain);
1624
1625 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1626 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "BootFile", pThis->pszBootFile);
1627 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "NextServer", pThis->pszNextServer);
1628
1629 int fDNSProxy = 0;
1630 GET_S32(rc, pDrvIns, pCfg, "DNSProxy", fDNSProxy);
1631 int fUseHostResolver = 0;
1632 GET_S32(rc, pDrvIns, pCfg, "UseHostResolver", fUseHostResolver);
1633 int MTU = 1500;
1634 GET_S32(rc, pDrvIns, pCfg, "SlirpMTU", MTU);
1635 int i32AliasMode = 0;
1636 int i32MainAliasMode = 0;
1637 GET_S32(rc, pDrvIns, pCfg, "AliasMode", i32MainAliasMode);
1638 int iIcmpCacheLimit = 100;
1639 GET_S32(rc, pDrvIns, pCfg, "ICMPCacheLimit", iIcmpCacheLimit);
1640 bool fLocalhostReachable = false;
1641 GET_BOOL(rc, pDrvIns, pCfg, "LocalhostReachable", fLocalhostReachable);
1642
1643 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1644 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1645 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1646 int i32SoMaxConn = 10;
1647 GET_S32(rc, pDrvIns, pCfg, "SoMaxConnection", i32SoMaxConn);
1648 /*
1649 * Query the network port interface.
1650 */
1651 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1652 if (!pThis->pIAboveNet)
1653 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1654 N_("Configuration error: the above device/driver didn't "
1655 "export the network port interface"));
1656 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1657 if (!pThis->pIAboveConfig)
1658 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1659 N_("Configuration error: the above device/driver didn't "
1660 "export the network config interface"));
1661
1662 /* Generate a network address for this network card. */
1663 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1664 GET_STRING(rc, pDrvIns, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1665 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1666 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: missing network"),
1667 pDrvIns->iInstance);
1668
1669 RTNETADDRIPV4 Network, Netmask;
1670
1671 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1672 if (RT_FAILURE(rc))
1673 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1674 N_("NAT#%d: Configuration error: network '%s' describes not a valid IPv4 network"),
1675 pDrvIns->iInstance, szNetwork);
1676
1677 /*
1678 * Initialize slirp.
1679 */
1680 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network.u), Netmask.u,
1681 fPassDomain, !!fUseHostResolver, i32AliasMode,
1682 iIcmpCacheLimit, fLocalhostReachable, pThis);
1683 if (RT_SUCCESS(rc))
1684 {
1685 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1686 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1687 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1688 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1689 slirp_set_mtu(pThis->pNATState, MTU);
1690 slirp_set_somaxconn(pThis->pNATState, i32SoMaxConn);
1691
1692 char *pszBindIP = NULL;
1693 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "BindIP", pszBindIP);
1694 slirp_set_binding_address(pThis->pNATState, pszBindIP);
1695 if (pszBindIP != NULL)
1696 PDMDrvHlpMMHeapFree(pDrvIns, pszBindIP);
1697
1698#define SLIRP_SET_TUNING_VALUE(name, setter) \
1699 do \
1700 { \
1701 int len = 0; \
1702 rc = pHlp->pfnCFGMQueryS32(pCfg, name, &len); \
1703 if (RT_SUCCESS(rc)) \
1704 setter(pThis->pNATState, len); \
1705 } while(0)
1706
1707 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1708 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1709 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1710 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1711
1712 slirp_register_statistics(pThis->pNATState, pDrvIns);
1713#ifdef VBOX_WITH_STATISTICS
1714# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1715# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1716# include "counters.h"
1717#endif
1718
1719#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1720 PCFGMNODE pMappingsCfg = pHlp->pfnCFGMGetChild(pCfg, "HostResolverMappings");
1721
1722 if (pMappingsCfg)
1723 {
1724 rc = drvNATConstructDNSMappings(pDrvIns->iInstance, pThis, pMappingsCfg);
1725 AssertRC(rc);
1726 }
1727#endif
1728 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, &Network);
1729 if (RT_SUCCESS(rc))
1730 {
1731 /*
1732 * Register a load done notification to get the MAC address into the slirp
1733 * engine after we loaded a guest state.
1734 */
1735 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1736 AssertLogRelRCReturn(rc, rc);
1737
1738 rc = RTReqQueueCreate(&pThis->hSlirpReqQueue);
1739 AssertLogRelRCReturn(rc, rc);
1740
1741 rc = RTReqQueueCreate(&pThis->hRecvReqQueue);
1742 AssertLogRelRCReturn(rc, rc);
1743
1744 rc = RTReqQueueCreate(&pThis->hUrgRecvReqQueue);
1745 AssertLogRelRCReturn(rc, rc);
1746
1747 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1748 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1749 AssertRCReturn(rc, rc);
1750
1751 rc = RTSemEventCreate(&pThis->EventRecv);
1752 AssertRCReturn(rc, rc);
1753
1754 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1755 AssertRCReturn(rc, rc);
1756
1757 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1758 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1759 AssertRCReturn(rc, rc);
1760
1761 rc = RTReqQueueCreate(&pThis->hHostResQueue);
1762 AssertRCReturn(rc, rc);
1763
1764#if defined(RT_OS_LINUX) && defined(RT_ARCH_ARM64)
1765 /* 64KiB stacks are not supported at least linux.arm64 (thread creation fails). */
1766 size_t const cbStack = _128K;
1767#else
1768 size_t const cbStack = _64K;
1769#endif
1770 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pHostResThread,
1771 pThis, drvNATHostResThread, drvNATHostResWakeup,
1772 cbStack, RTTHREADTYPE_IO, "HOSTRES");
1773 AssertRCReturn(rc, rc);
1774
1775 rc = RTCritSectInit(&pThis->DevAccessLock);
1776 AssertRCReturn(rc, rc);
1777
1778 rc = RTCritSectInit(&pThis->XmitLock);
1779 AssertRCReturn(rc, rc);
1780
1781 char szTmp[128];
1782 RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
1783 PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
1784
1785#ifndef RT_OS_WINDOWS
1786 /*
1787 * Create the control pipe.
1788 */
1789 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
1790 AssertRCReturn(rc, rc);
1791#else
1792 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1793 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1794 VBOX_WAKEUP_EVENT_INDEX);
1795#endif
1796
1797 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1798 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1799 AssertRCReturn(rc, rc);
1800
1801 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1802
1803#ifdef RT_OS_DARWIN
1804 /* Set up a watcher which notifies us everytime the DNS server changes. */
1805 int rc2 = VINF_SUCCESS;
1806 SCDynamicStoreContext SCDynStorCtx;
1807
1808 SCDynStorCtx.version = 0;
1809 SCDynStorCtx.info = pThis;
1810 SCDynStorCtx.retain = NULL;
1811 SCDynStorCtx.release = NULL;
1812 SCDynStorCtx.copyDescription = NULL;
1813
1814 SCDynamicStoreRef hDynStor = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.drvnat"), drvNatDnsChanged, &SCDynStorCtx);
1815 if (hDynStor)
1816 {
1817 CFRunLoopSourceRef hRunLoopSrc = SCDynamicStoreCreateRunLoopSource(NULL, hDynStor, 0);
1818 if (hRunLoopSrc)
1819 {
1820 CFStringRef aWatchKeys[] =
1821 {
1822 CFSTR("State:/Network/Global/DNS")
1823 };
1824 CFArrayRef hArray = CFArrayCreate(NULL, (const void **)aWatchKeys, 1, &kCFTypeArrayCallBacks);
1825
1826 if (hArray)
1827 {
1828 if (SCDynamicStoreSetNotificationKeys(hDynStor, hArray, NULL))
1829 {
1830 CFRunLoopRef hRunLoopMain = CFRunLoopGetMain();
1831 CFRetain(hRunLoopMain);
1832 CFRunLoopAddSource(hRunLoopMain, hRunLoopSrc, kCFRunLoopCommonModes);
1833 CFRelease(hRunLoopMain);
1834 pThis->hRunLoopSrcDnsWatcher = hRunLoopSrc;
1835 }
1836 else
1837 rc2 = VERR_NO_MEMORY;
1838
1839 CFRelease(hArray);
1840 }
1841 else
1842 rc2 = VERR_NO_MEMORY;
1843
1844 if (RT_FAILURE(rc2)) /* Keep the runloop source referenced for destruction. */
1845 CFRelease(hRunLoopSrc);
1846 }
1847 CFRelease(hDynStor);
1848 }
1849 else
1850 rc2 = VERR_NO_MEMORY;
1851
1852 if (RT_FAILURE(rc2))
1853 LogRel(("NAT#%d: Failed to install DNS change notifier. The guest might loose DNS access when switching networks on the host\n",
1854 pDrvIns->iInstance));
1855#endif
1856 return rc;
1857 }
1858
1859 /* failure path */
1860 slirp_term(pThis->pNATState);
1861 pThis->pNATState = NULL;
1862 }
1863 else
1864 {
1865 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1866 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1867 }
1868
1869 return rc;
1870}
1871
1872
1873/**
1874 * NAT network transport driver registration record.
1875 */
1876const PDMDRVREG g_DrvNAT =
1877{
1878 /* u32Version */
1879 PDM_DRVREG_VERSION,
1880 /* szName */
1881 "NAT",
1882 /* szRCMod */
1883 "",
1884 /* szR0Mod */
1885 "",
1886 /* pszDescription */
1887 "NAT Network Transport Driver",
1888 /* fFlags */
1889 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1890 /* fClass. */
1891 PDM_DRVREG_CLASS_NETWORK,
1892 /* cMaxInstances */
1893 ~0U,
1894 /* cbInstance */
1895 sizeof(DRVNAT),
1896 /* pfnConstruct */
1897 drvNATConstruct,
1898 /* pfnDestruct */
1899 drvNATDestruct,
1900 /* pfnRelocate */
1901 NULL,
1902 /* pfnIOCtl */
1903 NULL,
1904 /* pfnPowerOn */
1905 drvNATPowerOn,
1906 /* pfnReset */
1907 NULL,
1908 /* pfnSuspend */
1909 NULL,
1910 /* pfnResume */
1911 drvNATResume,
1912 /* pfnAttach */
1913 NULL,
1914 /* pfnDetach */
1915 NULL,
1916 /* pfnPowerOff */
1917 NULL,
1918 /* pfnSoftReset */
1919 NULL,
1920 /* u32EndVersion */
1921 PDM_DRVREG_VERSION
1922};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use