VirtualBox

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

Last change on this file was 104583, checked in by vboxsync, 2 months ago

Devices/Network/DrvNAT.cpp: Simplify drvNATRecvWakeup() a tiny bit, bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use