VirtualBox

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

Last change on this file since 57809 was 57784, checked in by vboxsync, 9 years ago

NAT: rewrite handling of port-forwarding.

The most interesting part is handling of wildcard guest address
(0.0.0.0) for which we are supposed to guess the real guest IP. For
TCP we delay the decision until new connection come and the we can use
the current guess for each new connection. For UDP things are
trickier. For now we set the current guess as the destination on
first incoming packet, but that doesn't handle changes of the guest
address or outgoing packets. This needs more thought.

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