VirtualBox

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

Last change on this file since 87766 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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