VirtualBox

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

Last change on this file since 28396 was 28396, checked in by vboxsync, 15 years ago

DrvNAT: minor cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.1 KB
Line 
1/* $Id: DrvNAT.cpp 28396 2010-04-16 07:37:03Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_NAT
27#define __STDC_LIMIT_MACROS
28#define __STDC_CONSTANT_MACROS
29#include "slirp/libslirp.h"
30#include "slirp/ctl.h"
31#include <VBox/pdmdrv.h>
32#include <VBox/pdmnetifs.h>
33#include <VBox/pdmnetinline.h>
34#include <iprt/assert.h>
35#include <iprt/file.h>
36#include <iprt/mem.h>
37#include <iprt/string.h>
38#include <iprt/critsect.h>
39#include <iprt/cidr.h>
40#include <iprt/stream.h>
41#include <iprt/uuid.h>
42
43#include "Builtins.h"
44
45#ifndef RT_OS_WINDOWS
46# include <unistd.h>
47# include <fcntl.h>
48# include <poll.h>
49# include <errno.h>
50#endif
51#ifdef RT_OS_FREEBSD
52# include <netinet/in.h>
53#endif
54#include <iprt/semaphore.h>
55#include <iprt/req.h>
56
57#define COUNTERS_INIT
58#include "counters.h"
59
60
61/*******************************************************************************
62* Defined Constants And Macros *
63*******************************************************************************/
64
65/**
66 * @todo: This is a bad hack to prevent freezing the guest during high network
67 * activity. Windows host only. This needs to be fixed properly.
68 */
69#define VBOX_NAT_DELAY_HACK
70
71#define GET_EXTRADATA(pthis, node, name, rc, type, type_name, var) \
72do { \
73 (rc) = CFGMR3Query ## type((node), name, &(var)); \
74 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
75 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
76 (pthis)->pDrvIns->iInstance); \
77} while (0)
78
79#define GET_ED_STRICT(pthis, node, name, rc, type, type_name, var) \
80do { \
81 (rc) = CFGMR3Query ## type((node), name, &(var)); \
82 if (RT_FAILURE((rc))) \
83 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
84 (pthis)->pDrvIns->iInstance); \
85} while (0)
86
87#define GET_EXTRADATA_N(pthis, node, name, rc, type, type_name, var, var_size) \
88do { \
89 (rc) = CFGMR3Query ## type((node), name, &(var), var_size); \
90 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
91 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
92 (pthis)->pDrvIns->iInstance); \
93} while (0)
94
95#define GET_BOOL(rc, pthis, node, name, var) \
96 GET_EXTRADATA(pthis, node, name, (rc), Bool, bolean, (var))
97#define GET_STRING(rc, pthis, node, name, var, var_size) \
98 GET_EXTRADATA_N(pthis, node, name, (rc), String, string, (var), (var_size))
99#define GET_STRING_ALLOC(rc, pthis, node, name, var) \
100 GET_EXTRADATA(pthis, node, name, (rc), StringAlloc, string, (var))
101#define GET_S32(rc, pthis, node, name, var) \
102 GET_EXTRADATA(pthis, node, name, (rc), S32, int, (var))
103#define GET_S32_STRICT(rc, pthis, node, name, var) \
104 GET_ED_STRICT(pthis, node, name, (rc), S32, int, (var))
105
106
107
108#define DO_GET_IP(rc, node, instance, status, x) \
109do { \
110 char sz##x[32]; \
111 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
112 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
113 (status) = inet_aton(sz ## x, &x); \
114} while (0)
115
116#define GETIP_DEF(rc, node, instance, x, def) \
117do \
118{ \
119 int status = 0; \
120 DO_GET_IP((rc), (node), (instance), status, x); \
121 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
122 x.s_addr = def; \
123} while (0)
124
125/*******************************************************************************
126* Structures and Typedefs *
127*******************************************************************************/
128/**
129 * NAT network transport driver instance data.
130 *
131 * @implements PDMINETWORKUP
132 */
133typedef struct DRVNAT
134{
135 /** The network interface. */
136 PDMINETWORKUP INetworkUp;
137 /** The port we're attached to. */
138 PPDMINETWORKDOWN pIAboveNet;
139 /** The network config of the port we're attached to. */
140 PPDMINETWORKCONFIG pIAboveConfig;
141 /** Pointer to the driver instance. */
142 PPDMDRVINS pDrvIns;
143 /** Link state */
144 PDMNETWORKLINKSTATE enmLinkState;
145 /** NAT state for this instance. */
146 PNATState pNATState;
147 /** TFTP directory prefix. */
148 char *pszTFTPPrefix;
149 /** Boot file name to provide in the DHCP server response. */
150 char *pszBootFile;
151 /** tftp server name to provide in the DHCP server response. */
152 char *pszNextServer;
153 /** Polling thread. */
154 PPDMTHREAD pSlirpThread;
155 /** Queue for NAT-thread-external events. */
156 PRTREQQUEUE pSlirpReqQueue;
157 /** The guest IP for port-forwarding. */
158 uint32_t GuestIP;
159 uint32_t alignment1;
160
161#ifdef VBOX_WITH_SLIRP_MT
162 PPDMTHREAD pGuestThread;
163#endif
164#ifndef RT_OS_WINDOWS
165 /** The write end of the control pipe. */
166 RTFILE PipeWrite;
167 /** The read end of the control pipe. */
168 RTFILE PipeRead;
169# if HC_ARCH_BITS == 32
170 /** Alignment padding. */
171 uint32_t alignment2;
172# endif
173#else
174 /** for external notification */
175 HANDLE hWakeupEvent;
176#endif
177
178#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
179#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
180#include "counters.h"
181 /** thread delivering packets for receiving by the guest */
182 PPDMTHREAD pRecvThread;
183 /** thread delivering urg packets for receiving by the guest */
184 PPDMTHREAD pUrgRecvThread;
185 /** event to wakeup the guest receive thread */
186 RTSEMEVENT EventRecv;
187 /** event to wakeup the guest urgent receive thread */
188 RTSEMEVENT EventUrgRecv;
189 /** Receive Req queue (deliver packets to the guest) */
190 PRTREQQUEUE pRecvReqQueue;
191 /** Receive Urgent Req queue (deliver packets to the guest). */
192 PRTREQQUEUE pUrgRecvReqQueue;
193
194 /** makes access to device func RecvAvail and Recv atomical. */
195 RTCRITSECT DevAccessLock;
196 /** Number of in-flight urgent packets. */
197 volatile uint32_t cUrgPkts;
198 /** Number of in-flight regular packets. */
199 volatile uint32_t cPkts;
200
201 /** Transmit lock taken by BeginXmit and released by EndXmit. */
202 RTCRITSECT XmitLock;
203} DRVNAT;
204AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
205/** Pointer the NAT driver instance data. */
206typedef DRVNAT *PDRVNAT;
207
208
209/*******************************************************************************
210* Internal Functions *
211*******************************************************************************/
212static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho);
213
214
215static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
216{
217 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
218
219 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
220 return VINF_SUCCESS;
221
222 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
223 {
224 RTReqProcess(pThis->pRecvReqQueue, 0);
225 if (ASMAtomicReadU32(&pThis->cPkts) == 0)
226 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
227 }
228 return VINF_SUCCESS;
229}
230
231
232static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
233{
234 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
235 int rc;
236 rc = RTSemEventSignal(pThis->EventRecv);
237
238 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
239 return VINF_SUCCESS;
240}
241
242static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
243{
244 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
245
246 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
247 return VINF_SUCCESS;
248
249 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
250 {
251 RTReqProcess(pThis->pUrgRecvReqQueue, 0);
252 if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
253 {
254 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
255 AssertRC(rc);
256 }
257 }
258 return VINF_SUCCESS;
259}
260
261static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
262{
263 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
264 int rc = RTSemEventSignal(pThis->EventUrgRecv);
265 AssertRC(rc);
266
267 return VINF_SUCCESS;
268}
269
270static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
271{
272 int rc = RTCritSectEnter(&pThis->DevAccessLock);
273 AssertRC(rc);
274 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
275 if (RT_SUCCESS(rc))
276 {
277 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
278 AssertRC(rc);
279 }
280 else if ( rc != VERR_TIMEOUT
281 && rc != VERR_INTERRUPTED)
282 {
283 AssertRC(rc);
284 }
285
286 rc = RTCritSectLeave(&pThis->DevAccessLock);
287 AssertRC(rc);
288
289 slirp_ext_m_free(pThis->pNATState, m);
290#ifdef VBOX_WITH_SLIRP_BSD_MBUF
291 RTMemFree(pu8Buf);
292#endif
293 if (ASMAtomicDecU32(&pThis->cUrgPkts) == 0)
294 {
295 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
296 drvNATNotifyNATThread(pThis, "drvNATUrgRecvWorker");
297 }
298}
299
300
301static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
302{
303 int rc;
304 STAM_PROFILE_START(&pThis->StatNATRecv, a);
305
306 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
307
308 while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
309 {
310 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
311 if ( RT_FAILURE(rc)
312 && ( rc == VERR_TIMEOUT
313 || rc == VERR_INTERRUPTED))
314 goto done_unlocked;
315 }
316
317 rc = RTCritSectEnter(&pThis->DevAccessLock);
318 AssertRC(rc);
319
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
335done_unlocked:
336 slirp_ext_m_free(pThis->pNATState, m);
337#ifdef VBOX_WITH_SLIRP_BSD_MBUF
338 RTMemFree(pu8Buf);
339#endif
340 ASMAtomicDecU32(&pThis->cPkts);
341
342 drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
343
344 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
345 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
346}
347
348/**
349 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
350 *
351 * @param pThis Pointer to the NAT instance.
352 * @param pSgBuf The S/G buffer to free.
353 */
354static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
355{
356 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
357 pSgBuf->fFlags = 0;
358 if (pSgBuf->pvAllocator)
359 {
360 Assert(!pSgBuf->pvUser);
361 slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator);
362 pSgBuf->pvAllocator = NULL;
363 }
364 else if (pSgBuf->pvUser)
365 {
366 RTMemFree(pSgBuf->aSegs[0].pvSeg);
367 pSgBuf->aSegs[0].pvSeg = NULL;
368 RTMemFree(pSgBuf->pvUser);
369 pSgBuf->pvUser = NULL;
370 }
371 RTMemFree(pSgBuf);
372}
373
374/**
375 * Worker function for drvNATSend().
376 *
377 * @param pThis Pointer to the NAT instance.
378 * @param pSgBuf The scatter/gather buffer.
379 * @thread NAT
380 */
381static void drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
382{
383 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
384 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
385 {
386 struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
387 if (m)
388 {
389 /*
390 * A normal frame.
391 */
392 pSgBuf->pvAllocator = NULL;
393 slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
394 }
395 else
396 {
397 /*
398 * GSO frame, need to segment it.
399 */
400 /** @todo Make the NAT engine grok large frames? Could be more efficient... */
401#if 0 /* this is for testing PDMNetGsoCarveSegmentQD. */
402 uint8_t abHdrScratch[256];
403#endif
404 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
405 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
406 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
407 for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
408 {
409 size_t cbSeg;
410 void *pvSeg;
411 m = slirp_ext_m_get(pThis->pNATState, pGso->cbHdrs + pGso->cbMaxSeg, &pvSeg, &cbSeg);
412 if (!m)
413 break;
414
415#if 1
416 uint32_t cbPayload;
417 uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
418 iSeg, cSegs, (uint8_t *)pvSeg, &cbPayload);
419 memcpy((uint8_t *)pvSeg + pGso->cbHdrs, pbFrame + offPayload, cbPayload);
420
421 slirp_input(pThis->pNATState, m, cbPayload + pGso->cbHdrs);
422#else
423 uint32_t cbSegFrame;
424 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
425 iSeg, cSegs, &cbSegFrame);
426 memcpy((uint8_t *)pvSeg, pvSegFrame, cbSegFrame);
427
428 slirp_input(pThis->pNATState, m, cbSegFrame);
429#endif
430 }
431 }
432 }
433 drvNATFreeSgBuf(pThis, pSgBuf);
434
435 /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf sematics. */
436}
437
438/**
439 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
440 */
441static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
442{
443 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
444 int rc = RTCritSectTryEnter(&pThis->XmitLock);
445 if (RT_FAILURE(rc))
446 {
447 /** @todo Kick the worker thread when we have one... */
448 rc = VERR_TRY_AGAIN;
449 }
450 return rc;
451}
452
453/**
454 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
455 */
456static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
457 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
458{
459 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
460 Assert(RTCritSectIsOwner(&pThis->XmitLock));
461
462 /*
463 * Drop the incoming frame if the NAT thread isn't running.
464 */
465 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
466 {
467 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
468 return VERR_NET_NO_NETWORK;
469 }
470
471 /*
472 * Allocate a scatter/gather buffer and an mbuf.
473 */
474 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
475 if (!pSgBuf)
476 return VERR_NO_MEMORY;
477 if (!pGso)
478 {
479 pSgBuf->pvUser = NULL;
480 pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
481 &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
482 if (!pSgBuf->pvAllocator)
483 {
484 RTMemFree(pSgBuf);
485 /** @todo Implement the VERR_TRY_AGAIN semantics. */
486 return VERR_NO_MEMORY;
487 }
488 }
489 else
490 {
491 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
492 pSgBuf->pvAllocator = NULL;
493 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
494 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
495 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
496 {
497 RTMemFree(pSgBuf->aSegs[0].pvSeg);
498 RTMemFree(pSgBuf->pvUser);
499 RTMemFree(pSgBuf);
500 /** @todo Implement the VERR_TRY_AGAIN semantics. */
501 return VERR_NO_MEMORY;
502 }
503 }
504
505 /*
506 * Initialize the S/G buffer and return.
507 */
508 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
509 pSgBuf->cbUsed = 0;
510 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
511 pSgBuf->cSegs = 1;
512
513#if 0 /* poison */
514 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
515#endif
516 *ppSgBuf = pSgBuf;
517 return VINF_SUCCESS;
518}
519
520/**
521 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
522 */
523static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
524{
525 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
526 Assert(RTCritSectIsOwner(&pThis->XmitLock));
527 drvNATFreeSgBuf(pThis, pSgBuf);
528 return VINF_SUCCESS;
529}
530
531/**
532 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
533 */
534static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
535{
536 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
537 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
538 Assert(RTCritSectIsOwner(&pThis->XmitLock));
539
540 int rc;
541 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
542 {
543#ifdef VBOX_WITH_SLIRP_MT
544 PRTREQQUEUE pQueue = (PRTREQQUEUE)slirp_get_queue(pThis->pNATState);
545#else
546 PRTREQQUEUE pQueue = pThis->pSlirpReqQueue;
547#endif
548 rc = RTReqCallEx(pQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
549 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
550 if (RT_SUCCESS(rc))
551 {
552 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
553 return VINF_SUCCESS;
554 }
555
556 rc = VERR_NET_NO_BUFFER_SPACE;
557 }
558 else
559 rc = VERR_NET_DOWN;
560 drvNATFreeSgBuf(pThis, pSgBuf);
561 return rc;
562}
563
564/**
565 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
566 */
567static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
568{
569 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
570 RTCritSectLeave(&pThis->XmitLock);
571}
572
573/**
574 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
575 */
576static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
577{
578 int rc;
579#ifndef RT_OS_WINDOWS
580 /* kick poll() */
581 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
582#else
583 /* kick WSAWaitForMultipleEvents */
584 rc = WSASetEvent(pThis->hWakeupEvent);
585#endif
586 AssertRC(rc);
587}
588
589/**
590 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
591 */
592static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
593{
594 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
595 /* nothing to do */
596}
597
598/**
599 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
600 * @thread "NAT" thread.
601 */
602static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
603{
604 pThis->enmLinkState = enmLinkState;
605
606 switch (enmLinkState)
607 {
608 case PDMNETWORKLINKSTATE_UP:
609 LogRel(("NAT: link up\n"));
610 slirp_link_up(pThis->pNATState);
611 break;
612
613 case PDMNETWORKLINKSTATE_DOWN:
614 case PDMNETWORKLINKSTATE_DOWN_RESUME:
615 LogRel(("NAT: link down\n"));
616 slirp_link_down(pThis->pNATState);
617 break;
618
619 default:
620 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
621 }
622}
623
624/**
625 * Notification on link status changes.
626 *
627 * @param pInterface Pointer to the interface structure containing the called function pointer.
628 * @param enmLinkState The new link state.
629 * @thread EMT
630 */
631static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
632{
633 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
634
635 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
636
637 /* don't queue new requests when the NAT thread is about to stop */
638 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
639 return;
640
641 PRTREQ pReq;
642 int rc = RTReqCallEx(pThis->pSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
643 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
644 if (RT_LIKELY(rc == VERR_TIMEOUT))
645 {
646 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
647 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
648 AssertRC(rc);
649 }
650 else
651 AssertRC(rc);
652 RTReqFree(pReq);
653}
654
655/**
656 * NAT thread handling the slirp stuff.
657 *
658 * The slirp implementation is single-threaded so we execute this enginre in a
659 * dedicated thread. We take care that this thread does not become the
660 * bottleneck: If the guest wants to send, a request is enqueued into the
661 * pSlirpReqQueue and handled asynchronously by this thread. If this thread
662 * wants to deliver packets to the guest, it enqueues a request into
663 * pRecvReqQueue which is later handled by the Recv thread.
664 */
665static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
666{
667 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
668 int nFDs = -1;
669 int ms;
670#ifdef RT_OS_WINDOWS
671 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
672 unsigned int cBreak = 0;
673#else /* RT_OS_WINDOWS */
674 unsigned int cPollNegRet = 0;
675#endif /* !RT_OS_WINDOWS */
676
677 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
678
679 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
680 return VINF_SUCCESS;
681
682 /*
683 * Polling loop.
684 */
685 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
686 {
687 /*
688 * To prevent concurent execution of sending/receving threads
689 */
690#ifndef RT_OS_WINDOWS
691 nFDs = slirp_get_nsock(pThis->pNATState);
692 /* allocation for all sockets + Management pipe */
693 struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
694 if (polls == NULL)
695 return VERR_NO_MEMORY;
696
697 /* don't pass the managemant pipe */
698 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
699
700 polls[0].fd = pThis->PipeRead;
701 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
702 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
703 polls[0].revents = 0;
704
705 int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
706 if (cChangedFDs < 0)
707 {
708 if (errno == EINTR)
709 {
710 Log2(("NAT: signal was caught while sleep on poll\n"));
711 /* No error, just process all outstanding requests but don't wait */
712 cChangedFDs = 0;
713 }
714 else if (cPollNegRet++ > 128)
715 {
716 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
717 cPollNegRet = 0;
718 }
719 }
720
721 if (cChangedFDs >= 0)
722 {
723 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
724 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
725 {
726 /* drain the pipe */
727 char ch[1];
728 size_t cbRead;
729 int counter = 0;
730 /*
731 * drvNATSend decoupled so we don't know how many times
732 * device's thread sends before we've entered multiplex,
733 * so to avoid false alarm drain pipe here to the very end
734 *
735 * @todo: Probably we should counter drvNATSend to count how
736 * deep pipe has been filed before drain.
737 *
738 * XXX:Make it reading exactly we need to drain the pipe.
739 */
740 /** @todo use RTPipeCreate + RTPipeRead(,biggerbuffer) here, it's
741 * non-blocking. */
742 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
743 }
744 }
745 /* process _all_ outstanding requests but don't wait */
746 RTReqProcess(pThis->pSlirpReqQueue, 0);
747 RTMemFree(polls);
748
749#else /* RT_OS_WINDOWS */
750 nFDs = -1;
751 slirp_select_fill(pThis->pNATState, &nFDs);
752 DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
753 slirp_get_timeout_ms(pThis->pNATState),
754 FALSE);
755 if ( (dwEvent < WSA_WAIT_EVENT_0 || dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
756 && dwEvent != WSA_WAIT_TIMEOUT)
757 {
758 int error = WSAGetLastError();
759 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
760 RTAssertPanic();
761 }
762
763 if (dwEvent == WSA_WAIT_TIMEOUT)
764 {
765 /* only check for slow/fast timers */
766 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
767 continue;
768 }
769 /* poll the sockets in any case */
770 Log2(("%s: poll\n", __FUNCTION__));
771 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(dwEvent == WSA_WAIT_EVENT_0));
772 /* process _all_ outstanding requests but don't wait */
773 RTReqProcess(pThis->pSlirpReqQueue, 0);
774# ifdef VBOX_NAT_DELAY_HACK
775 if (cBreak++ > 128)
776 {
777 cBreak = 0;
778 RTThreadSleep(2);
779 }
780# endif
781#endif /* RT_OS_WINDOWS */
782 }
783
784 return VINF_SUCCESS;
785}
786
787
788/**
789 * Unblock the send thread so it can respond to a state change.
790 *
791 * @returns VBox status code.
792 * @param pDevIns The pcnet device instance.
793 * @param pThread The send thread.
794 */
795static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
796{
797 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
798
799 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
800 return VINF_SUCCESS;
801}
802
803#ifdef VBOX_WITH_SLIRP_MT
804
805static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
806{
807 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
808
809 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
810 return VINF_SUCCESS;
811
812 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
813 slirp_process_queue(pThis->pNATState);
814
815 return VINF_SUCCESS;
816}
817
818
819static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
820{
821 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
822
823 return VINF_SUCCESS;
824}
825
826#endif /* VBOX_WITH_SLIRP_MT */
827
828/**
829 * Function called by slirp to check if it's possible to feed incoming data to the network port.
830 * @returns 1 if possible.
831 * @returns 0 if not possible.
832 */
833int slirp_can_output(void *pvUser)
834{
835 return 1;
836}
837
838void slirp_push_recv_thread(void *pvUser)
839{
840 PDRVNAT pThis = (PDRVNAT)pvUser;
841 Assert(pThis);
842 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
843}
844
845void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
846{
847 PDRVNAT pThis = (PDRVNAT)pvUser;
848 Assert(pThis);
849
850 PRTREQ pReq = NULL;
851
852 /* don't queue new requests when the NAT thread is about to stop */
853 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
854 return;
855
856 ASMAtomicIncU32(&pThis->cUrgPkts);
857 int rc = RTReqCallEx(pThis->pUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
858 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
859 AssertRC(rc);
860 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
861}
862
863/**
864 * Function called by slirp to feed incoming data to the NIC.
865 */
866void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
867{
868 PDRVNAT pThis = (PDRVNAT)pvUser;
869 Assert(pThis);
870
871 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
872 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
873
874 PRTREQ pReq = NULL;
875
876 /* don't queue new requests when the NAT thread is about to stop */
877 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
878 return;
879
880 ASMAtomicIncU32(&pThis->cPkts);
881 int rc = RTReqCallEx(pThis->pRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
882 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
883 AssertRC(rc);
884 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
885 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
886}
887
888
889/**
890 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
891 */
892static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
893{
894 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
895 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
896
897 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
898 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
899 return NULL;
900}
901
902
903/**
904 * Get the MAC address into the slirp stack.
905 *
906 * Called by drvNATLoadDone and drvNATPowerOn.
907 */
908static void drvNATSetMac(PDRVNAT pThis)
909{
910 if (pThis->pIAboveConfig)
911 {
912 RTMAC Mac;
913 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
914 /* Re-activate the port forwarding. If */
915 slirp_set_ethaddr_and_activate_port_forwarding(pThis->pNATState, Mac.au8, pThis->GuestIP);
916 }
917}
918
919
920/**
921 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
922 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
923 * (usually done during guest boot).
924 */
925static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
926{
927 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
928 drvNATSetMac(pThis);
929 return VINF_SUCCESS;
930}
931
932
933/**
934 * Some guests might not use DHCP to retrieve an IP but use a static IP.
935 */
936static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
937{
938 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
939 drvNATSetMac(pThis);
940}
941
942
943/**
944 * Sets up the redirectors.
945 *
946 * @returns VBox status code.
947 * @param pCfg The configuration handle.
948 */
949static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network)
950{
951 RTMAC Mac;
952 RT_ZERO(Mac); /* can't get MAC here */
953
954 /*
955 * Enumerate redirections.
956 */
957 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
958 {
959 /*
960 * Validate the port forwarding config.
961 */
962 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
963 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
964
965 /* protocol type */
966 bool fUDP;
967 char szProtocol[32];
968 int rc;
969 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
970 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
971 {
972 fUDP = false;
973 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
974 }
975 else if (RT_SUCCESS(rc))
976 {
977 if (!RTStrICmp(szProtocol, "TCP"))
978 fUDP = false;
979 else if (!RTStrICmp(szProtocol, "UDP"))
980 fUDP = true;
981 else
982 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
983 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
984 iInstance, szProtocol);
985 }
986 /* host port */
987 int32_t iHostPort;
988 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
989
990 /* guest port */
991 int32_t iGuestPort;
992 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
993
994 /* guest address */
995 struct in_addr GuestIP;
996 /* @todo (vvl) use CTL_* */
997 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
998
999 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
1000 * is not documented. Without */
1001 if (pThis->GuestIP == INADDR_ANY)
1002 pThis->GuestIP = GuestIP.s_addr;
1003
1004 /*
1005 * Call slirp about it.
1006 */
1007 struct in_addr BindIP;
1008 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
1009 if (slirp_redir(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
1010 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1011 N_("NAT#%d: configuration error: failed to set up "
1012 "redirection of %d to %d. Probably a conflict with "
1013 "existing services or other rules"), iInstance, iHostPort,
1014 iGuestPort);
1015 } /* for each redir rule */
1016
1017 return VINF_SUCCESS;
1018}
1019
1020
1021/**
1022 * Destruct a driver instance.
1023 *
1024 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1025 * resources can be freed correctly.
1026 *
1027 * @param pDrvIns The driver instance data.
1028 */
1029static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1030{
1031 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1032 LogFlow(("drvNATDestruct:\n"));
1033 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1034
1035 if (pThis->pNATState)
1036 {
1037 slirp_term(pThis->pNATState);
1038 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1039#ifdef VBOX_WITH_STATISTICS
1040# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1041# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1042# include "counters.h"
1043#endif
1044 pThis->pNATState = NULL;
1045 }
1046
1047 RTReqDestroyQueue(pThis->pSlirpReqQueue);
1048 pThis->pSlirpReqQueue = NULL;
1049
1050 RTReqDestroyQueue(pThis->pUrgRecvReqQueue);
1051 pThis->pUrgRecvReqQueue = NULL;
1052
1053 RTSemEventDestroy(pThis->EventRecv);
1054 pThis->EventRecv = NIL_RTSEMEVENT;
1055
1056 RTSemEventDestroy(pThis->EventUrgRecv);
1057 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1058
1059 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1060 RTCritSectDelete(&pThis->DevAccessLock);
1061
1062 if (RTCritSectIsInitialized(&pThis->XmitLock))
1063 RTCritSectDelete(&pThis->XmitLock);
1064}
1065
1066
1067/**
1068 * Construct a NAT network transport driver instance.
1069 *
1070 * @copydoc FNPDMDRVCONSTRUCT
1071 */
1072static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1073{
1074 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1075 LogFlow(("drvNATConstruct:\n"));
1076 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1077
1078 /*
1079 * Validate the config.
1080 */
1081 if (!CFGMR3AreValuesValid(pCfg,
1082 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1083 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1084 "SlirpMTU\0"
1085 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"))
1086 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1087 N_("Unknown NAT configuration option, only supports PassDomain,"
1088 " TFTPPrefix, BootFile and Network"));
1089
1090 /*
1091 * Init the static parts.
1092 */
1093 pThis->pDrvIns = pDrvIns;
1094 pThis->pNATState = NULL;
1095 pThis->pszTFTPPrefix = NULL;
1096 pThis->pszBootFile = NULL;
1097 pThis->pszNextServer = NULL;
1098 pThis->pSlirpReqQueue = NULL;
1099 pThis->pUrgRecvReqQueue = NULL;
1100 pThis->EventRecv = NIL_RTSEMEVENT;
1101 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1102
1103 /* IBase */
1104 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1105
1106 /* INetwork */
1107 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1108 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1109 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1110 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1111 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1112 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1113 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1114
1115 /*
1116 * Get the configuration settings.
1117 */
1118 int rc;
1119 bool fPassDomain = true;
1120 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1121
1122 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1123 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1124 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1125
1126 int fDNSProxy = 0;
1127 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1128 int fUseHostResolver = 0;
1129 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1130#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1131 int MTU = 1500;
1132 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1133#endif
1134
1135 /*
1136 * Query the network port interface.
1137 */
1138 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1139 if (!pThis->pIAboveNet)
1140 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1141 N_("Configuration error: the above device/driver didn't "
1142 "export the network port interface"));
1143 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1144 if (!pThis->pIAboveConfig)
1145 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1146 N_("Configuration error: the above device/driver didn't "
1147 "export the network config interface"));
1148
1149 /* Generate a network address for this network card. */
1150 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1151 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1152 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1153 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1154 "missing network"),
1155 pDrvIns->iInstance, szNetwork);
1156
1157 RTIPV4ADDR Network;
1158 RTIPV4ADDR Netmask;
1159 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1160 if (RT_FAILURE(rc))
1161 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1162 "network '%s' describes not a valid IPv4 network"),
1163 pDrvIns->iInstance, szNetwork);
1164
1165 /*
1166 * Initialize slirp.
1167 */
1168 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask,
1169 fPassDomain, !!fUseHostResolver, pThis);
1170 if (RT_SUCCESS(rc))
1171 {
1172 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1173 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1174 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1175 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1176#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1177 slirp_set_mtu(pThis->pNATState, MTU);
1178#endif
1179 char *pszBindIP = NULL;
1180 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1181 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1182 if (rc != 0)
1183 LogRel(("NAT: value of BindIP has been ignored\n"));
1184
1185 if(pszBindIP != NULL)
1186 MMR3HeapFree(pszBindIP);
1187#define SLIRP_SET_TUNING_VALUE(name, setter) \
1188 do \
1189 { \
1190 int len = 0; \
1191 rc = CFGMR3QueryS32(pCfg, name, &len); \
1192 if (RT_SUCCESS(rc)) \
1193 setter(pThis->pNATState, len); \
1194 } while(0)
1195
1196 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1197 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1198 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1199 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1200
1201 slirp_register_statistics(pThis->pNATState, pDrvIns);
1202#ifdef VBOX_WITH_STATISTICS
1203# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1204# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1205# include "counters.h"
1206#endif
1207
1208 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1209 if (RT_SUCCESS(rc))
1210 {
1211 /*
1212 * Register a load done notification to get the MAC address into the slirp
1213 * engine after we loaded a guest state.
1214 */
1215 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1216 AssertRCReturn(rc, rc);
1217
1218 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
1219 if (RT_FAILURE(rc))
1220 {
1221 LogRel(("NAT: Can't create request queue\n"));
1222 return rc;
1223 }
1224
1225 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1226 if (RT_FAILURE(rc))
1227 {
1228 LogRel(("NAT: Can't create request queue\n"));
1229 return rc;
1230 }
1231
1232 rc = RTReqCreateQueue(&pThis->pUrgRecvReqQueue);
1233 if (RT_FAILURE(rc))
1234 {
1235 LogRel(("NAT: Can't create request queue\n"));
1236 return rc;
1237 }
1238
1239 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1240 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1241 AssertRCReturn(rc, rc);
1242
1243 rc = RTSemEventCreate(&pThis->EventRecv);
1244 AssertRCReturn(rc, rc);
1245
1246 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1247 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1248 AssertRCReturn(rc, rc);
1249
1250 rc = RTSemEventCreate(&pThis->EventRecv);
1251 AssertRCReturn(rc, rc);
1252
1253 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1254 AssertRCReturn(rc, rc);
1255
1256 rc = RTCritSectInit(&pThis->DevAccessLock);
1257 AssertRCReturn(rc, rc);
1258
1259 rc = RTCritSectInit(&pThis->XmitLock);
1260 AssertRCReturn(rc, rc);
1261
1262#ifndef RT_OS_WINDOWS
1263 /*
1264 * Create the control pipe.
1265 */
1266 int fds[2];
1267 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1268 {
1269 rc = RTErrConvertFromErrno(errno);
1270 AssertRC(rc);
1271 return rc;
1272 }
1273 pThis->PipeRead = fds[0];
1274 pThis->PipeWrite = fds[1];
1275#else
1276 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1277 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1278 VBOX_WAKEUP_EVENT_INDEX);
1279#endif
1280
1281 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1282 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1283 AssertRC(rc);
1284
1285#ifdef VBOX_WITH_SLIRP_MT
1286 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1287 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1288 AssertRC(rc);
1289#endif
1290
1291 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
1292
1293 /* might return VINF_NAT_DNS */
1294 return rc;
1295 }
1296
1297 /* failure path */
1298 slirp_term(pThis->pNATState);
1299 pThis->pNATState = NULL;
1300 }
1301 else
1302 {
1303 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1304 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1305 }
1306
1307 return rc;
1308}
1309
1310
1311/**
1312 * NAT network transport driver registration record.
1313 */
1314const PDMDRVREG g_DrvNAT =
1315{
1316 /* u32Version */
1317 PDM_DRVREG_VERSION,
1318 /* szName */
1319 "NAT",
1320 /* szRCMod */
1321 "",
1322 /* szR0Mod */
1323 "",
1324 /* pszDescription */
1325 "NAT Network Transport Driver",
1326 /* fFlags */
1327 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1328 /* fClass. */
1329 PDM_DRVREG_CLASS_NETWORK,
1330 /* cMaxInstances */
1331 16,
1332 /* cbInstance */
1333 sizeof(DRVNAT),
1334 /* pfnConstruct */
1335 drvNATConstruct,
1336 /* pfnDestruct */
1337 drvNATDestruct,
1338 /* pfnRelocate */
1339 NULL,
1340 /* pfnIOCtl */
1341 NULL,
1342 /* pfnPowerOn */
1343 drvNATPowerOn,
1344 /* pfnReset */
1345 NULL,
1346 /* pfnSuspend */
1347 NULL,
1348 /* pfnResume */
1349 NULL,
1350 /* pfnAttach */
1351 NULL,
1352 /* pfnDetach */
1353 NULL,
1354 /* pfnPowerOff */
1355 NULL,
1356 /* pfnSoftReset */
1357 NULL,
1358 /* u32EndVersion */
1359 PDM_DRVREG_VERSION
1360};
1361
Note: See TracBrowser for help on using the repository browser.

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