VirtualBox

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

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

NAT/DrvNAT.cpp: introduces drvNATHostNetworkConfigurationChangeEventStrategySelector selecting strategy how to deal with Host Network change event and Host Resume. Function acting in following manner:

  • if Slirp configured as exposing host network configuration to guest, we proceed with "re-activating cable connection".
  • if Slirp configured as dns proxy, we reconfigure internal DNS state.
  • if Slirp configured to use host resolver, we should just ignore event.

currently this function called from couple places: drvNATResume and Host Network change event "listener" (darwin only). This will be changed in the future, when notification of host network change will be finished in Main, and we can drop OS dependent part in DrvNat, processing these events in the same way as port-forwarding done.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette