VirtualBox

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

Last change on this file since 51150 was 50951, checked in by vboxsync, 11 years ago

NAT: Rename drvNATHostNetworkConfigurationChangeEventStrategySelector
to less highfalutin and more clear drvNATUpdateDNS.

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