VirtualBox

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

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

NAT: introduces management routines of NAT libalias core. (see xTracker/#4843 for details)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.4 KB
Line 
1/* $Id: DrvNAT.cpp 28787 2010-04-27 06:36:48Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * 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#ifdef RT_OS_WINDOWS
670 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
671 unsigned int cBreak = 0;
672#else /* RT_OS_WINDOWS */
673 unsigned int cPollNegRet = 0;
674#endif /* !RT_OS_WINDOWS */
675
676 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
677
678 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
679 return VINF_SUCCESS;
680
681 /*
682 * Polling loop.
683 */
684 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
685 {
686 /*
687 * To prevent concurent execution of sending/receving threads
688 */
689#ifndef RT_OS_WINDOWS
690 nFDs = slirp_get_nsock(pThis->pNATState);
691 /* allocation for all sockets + Management pipe */
692 struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
693 if (polls == NULL)
694 return VERR_NO_MEMORY;
695
696 /* don't pass the managemant pipe */
697 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
698
699 polls[0].fd = pThis->PipeRead;
700 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
701 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
702 polls[0].revents = 0;
703
704 int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
705 if (cChangedFDs < 0)
706 {
707 if (errno == EINTR)
708 {
709 Log2(("NAT: signal was caught while sleep on poll\n"));
710 /* No error, just process all outstanding requests but don't wait */
711 cChangedFDs = 0;
712 }
713 else if (cPollNegRet++ > 128)
714 {
715 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
716 cPollNegRet = 0;
717 }
718 }
719
720 if (cChangedFDs >= 0)
721 {
722 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
723 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
724 {
725 /* drain the pipe */
726 char ch[1];
727 size_t cbRead;
728 int counter = 0;
729 /*
730 * drvNATSend decoupled so we don't know how many times
731 * device's thread sends before we've entered multiplex,
732 * so to avoid false alarm drain pipe here to the very end
733 *
734 * @todo: Probably we should counter drvNATSend to count how
735 * deep pipe has been filed before drain.
736 *
737 * XXX:Make it reading exactly we need to drain the pipe.
738 */
739 /** @todo use RTPipeCreate + RTPipeRead(,biggerbuffer) here, it's
740 * non-blocking. */
741 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
742 }
743 }
744 /* process _all_ outstanding requests but don't wait */
745 RTReqProcess(pThis->pSlirpReqQueue, 0);
746 RTMemFree(polls);
747
748#else /* RT_OS_WINDOWS */
749 nFDs = -1;
750 slirp_select_fill(pThis->pNATState, &nFDs);
751 DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
752 slirp_get_timeout_ms(pThis->pNATState),
753 FALSE);
754 if ( (dwEvent < WSA_WAIT_EVENT_0 || dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
755 && dwEvent != WSA_WAIT_TIMEOUT)
756 {
757 int error = WSAGetLastError();
758 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
759 RTAssertPanic();
760 }
761
762 if (dwEvent == WSA_WAIT_TIMEOUT)
763 {
764 /* only check for slow/fast timers */
765 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
766 continue;
767 }
768 /* poll the sockets in any case */
769 Log2(("%s: poll\n", __FUNCTION__));
770 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(dwEvent == WSA_WAIT_EVENT_0));
771 /* process _all_ outstanding requests but don't wait */
772 RTReqProcess(pThis->pSlirpReqQueue, 0);
773# ifdef VBOX_NAT_DELAY_HACK
774 if (cBreak++ > 128)
775 {
776 cBreak = 0;
777 RTThreadSleep(2);
778 }
779# endif
780#endif /* RT_OS_WINDOWS */
781 }
782
783 return VINF_SUCCESS;
784}
785
786
787/**
788 * Unblock the send thread so it can respond to a state change.
789 *
790 * @returns VBox status code.
791 * @param pDevIns The pcnet device instance.
792 * @param pThread The send thread.
793 */
794static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
795{
796 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
797
798 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
799 return VINF_SUCCESS;
800}
801
802#ifdef VBOX_WITH_SLIRP_MT
803
804static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
805{
806 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
807
808 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
809 return VINF_SUCCESS;
810
811 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
812 slirp_process_queue(pThis->pNATState);
813
814 return VINF_SUCCESS;
815}
816
817
818static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
819{
820 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
821
822 return VINF_SUCCESS;
823}
824
825#endif /* VBOX_WITH_SLIRP_MT */
826
827/**
828 * Function called by slirp to check if it's possible to feed incoming data to the network port.
829 * @returns 1 if possible.
830 * @returns 0 if not possible.
831 */
832int slirp_can_output(void *pvUser)
833{
834 return 1;
835}
836
837void slirp_push_recv_thread(void *pvUser)
838{
839 PDRVNAT pThis = (PDRVNAT)pvUser;
840 Assert(pThis);
841 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
842}
843
844void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
845{
846 PDRVNAT pThis = (PDRVNAT)pvUser;
847 Assert(pThis);
848
849 PRTREQ pReq = NULL;
850
851 /* don't queue new requests when the NAT thread is about to stop */
852 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
853 return;
854
855 ASMAtomicIncU32(&pThis->cUrgPkts);
856 int rc = RTReqCallEx(pThis->pUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
857 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
858 AssertRC(rc);
859 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
860}
861
862/**
863 * Function called by slirp to feed incoming data to the NIC.
864 */
865void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
866{
867 PDRVNAT pThis = (PDRVNAT)pvUser;
868 Assert(pThis);
869
870 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
871 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
872
873 PRTREQ pReq = NULL;
874
875 /* don't queue new requests when the NAT thread is about to stop */
876 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
877 return;
878
879 ASMAtomicIncU32(&pThis->cPkts);
880 int rc = RTReqCallEx(pThis->pRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
881 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
882 AssertRC(rc);
883 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
884 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
885}
886
887
888/**
889 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
890 */
891static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
892{
893 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
894 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
895
896 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
897 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
898 return NULL;
899}
900
901
902/**
903 * Get the MAC address into the slirp stack.
904 *
905 * Called by drvNATLoadDone and drvNATPowerOn.
906 */
907static void drvNATSetMac(PDRVNAT pThis)
908{
909 if (pThis->pIAboveConfig)
910 {
911 RTMAC Mac;
912 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
913 /* Re-activate the port forwarding. If */
914 slirp_set_ethaddr_and_activate_port_forwarding(pThis->pNATState, Mac.au8, pThis->GuestIP);
915 }
916}
917
918
919/**
920 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
921 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
922 * (usually done during guest boot).
923 */
924static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
925{
926 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
927 drvNATSetMac(pThis);
928 return VINF_SUCCESS;
929}
930
931
932/**
933 * Some guests might not use DHCP to retrieve an IP but use a static IP.
934 */
935static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
936{
937 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
938 drvNATSetMac(pThis);
939}
940
941
942/**
943 * Sets up the redirectors.
944 *
945 * @returns VBox status code.
946 * @param pCfg The configuration handle.
947 */
948static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network)
949{
950 RTMAC Mac;
951 RT_ZERO(Mac); /* can't get MAC here */
952
953 /*
954 * Enumerate redirections.
955 */
956 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
957 {
958 /*
959 * Validate the port forwarding config.
960 */
961 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
962 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
963
964 /* protocol type */
965 bool fUDP;
966 char szProtocol[32];
967 int rc;
968 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
969 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
970 {
971 fUDP = false;
972 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
973 }
974 else if (RT_SUCCESS(rc))
975 {
976 if (!RTStrICmp(szProtocol, "TCP"))
977 fUDP = false;
978 else if (!RTStrICmp(szProtocol, "UDP"))
979 fUDP = true;
980 else
981 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
982 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
983 iInstance, szProtocol);
984 }
985 /* host port */
986 int32_t iHostPort;
987 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
988
989 /* guest port */
990 int32_t iGuestPort;
991 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
992
993 /* guest address */
994 struct in_addr GuestIP;
995 /* @todo (vvl) use CTL_* */
996 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
997
998 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
999 * is not documented. Without */
1000 if (pThis->GuestIP == INADDR_ANY)
1001 pThis->GuestIP = GuestIP.s_addr;
1002
1003 /*
1004 * Call slirp about it.
1005 */
1006 struct in_addr BindIP;
1007 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
1008 if (slirp_redir(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
1009 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1010 N_("NAT#%d: configuration error: failed to set up "
1011 "redirection of %d to %d. Probably a conflict with "
1012 "existing services or other rules"), iInstance, iHostPort,
1013 iGuestPort);
1014 } /* for each redir rule */
1015
1016 return VINF_SUCCESS;
1017}
1018
1019
1020/**
1021 * Destruct a driver instance.
1022 *
1023 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1024 * resources can be freed correctly.
1025 *
1026 * @param pDrvIns The driver instance data.
1027 */
1028static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1029{
1030 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1031 LogFlow(("drvNATDestruct:\n"));
1032 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1033
1034 if (pThis->pNATState)
1035 {
1036 slirp_term(pThis->pNATState);
1037 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1038#ifdef VBOX_WITH_STATISTICS
1039# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1040# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1041# include "counters.h"
1042#endif
1043 pThis->pNATState = NULL;
1044 }
1045
1046 RTReqDestroyQueue(pThis->pSlirpReqQueue);
1047 pThis->pSlirpReqQueue = NULL;
1048
1049 RTReqDestroyQueue(pThis->pUrgRecvReqQueue);
1050 pThis->pUrgRecvReqQueue = NULL;
1051
1052 RTSemEventDestroy(pThis->EventRecv);
1053 pThis->EventRecv = NIL_RTSEMEVENT;
1054
1055 RTSemEventDestroy(pThis->EventUrgRecv);
1056 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1057
1058 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1059 RTCritSectDelete(&pThis->DevAccessLock);
1060
1061 if (RTCritSectIsInitialized(&pThis->XmitLock))
1062 RTCritSectDelete(&pThis->XmitLock);
1063}
1064
1065
1066/**
1067 * Construct a NAT network transport driver instance.
1068 *
1069 * @copydoc FNPDMDRVCONSTRUCT
1070 */
1071static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1072{
1073 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1074 LogFlow(("drvNATConstruct:\n"));
1075 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1076
1077 /*
1078 * Validate the config.
1079 */
1080 if (!CFGMR3AreValuesValid(pCfg,
1081 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1082 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1083 "SlirpMTU\0AliasMode\0"
1084 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"))
1085 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1086 N_("Unknown NAT configuration option, only supports PassDomain,"
1087 " TFTPPrefix, BootFile and Network"));
1088
1089 /*
1090 * Init the static parts.
1091 */
1092 pThis->pDrvIns = pDrvIns;
1093 pThis->pNATState = NULL;
1094 pThis->pszTFTPPrefix = NULL;
1095 pThis->pszBootFile = NULL;
1096 pThis->pszNextServer = NULL;
1097 pThis->pSlirpReqQueue = NULL;
1098 pThis->pUrgRecvReqQueue = NULL;
1099 pThis->EventRecv = NIL_RTSEMEVENT;
1100 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1101
1102 /* IBase */
1103 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1104
1105 /* INetwork */
1106 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1107 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1108 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1109 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1110 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1111 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1112 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1113
1114 /*
1115 * Get the configuration settings.
1116 */
1117 int rc;
1118 bool fPassDomain = true;
1119 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1120
1121 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1122 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1123 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1124
1125 int fDNSProxy = 0;
1126 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1127 int fUseHostResolver = 0;
1128 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1129#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1130 int MTU = 1500;
1131 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1132#endif
1133 int i32AliasMode = 0;
1134 int i32MainAliasMode = 0;
1135 GET_S32(rc, pThis, pCfg, "AliasMode", i32MainAliasMode);
1136
1137 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1138 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1139 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1140 /*
1141 * Query the network port interface.
1142 */
1143 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1144 if (!pThis->pIAboveNet)
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 port interface"));
1148 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1149 if (!pThis->pIAboveConfig)
1150 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1151 N_("Configuration error: the above device/driver didn't "
1152 "export the network config interface"));
1153
1154 /* Generate a network address for this network card. */
1155 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1156 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1157 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1158 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1159 "missing network"),
1160 pDrvIns->iInstance, szNetwork);
1161
1162 RTIPV4ADDR Network;
1163 RTIPV4ADDR Netmask;
1164 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1165 if (RT_FAILURE(rc))
1166 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1167 "network '%s' describes not a valid IPv4 network"),
1168 pDrvIns->iInstance, szNetwork);
1169
1170 /*
1171 * Initialize slirp.
1172 */
1173 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask,
1174 fPassDomain, !!fUseHostResolver, i32AliasMode, pThis);
1175 if (RT_SUCCESS(rc))
1176 {
1177 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1178 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1179 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1180 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1181#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1182 slirp_set_mtu(pThis->pNATState, MTU);
1183#endif
1184 char *pszBindIP = NULL;
1185 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1186 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1187 if (rc != 0)
1188 LogRel(("NAT: value of BindIP has been ignored\n"));
1189
1190 if(pszBindIP != NULL)
1191 MMR3HeapFree(pszBindIP);
1192#define SLIRP_SET_TUNING_VALUE(name, setter) \
1193 do \
1194 { \
1195 int len = 0; \
1196 rc = CFGMR3QueryS32(pCfg, name, &len); \
1197 if (RT_SUCCESS(rc)) \
1198 setter(pThis->pNATState, len); \
1199 } while(0)
1200
1201 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1202 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1203 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1204 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1205
1206 slirp_register_statistics(pThis->pNATState, pDrvIns);
1207#ifdef VBOX_WITH_STATISTICS
1208# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1209# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1210# include "counters.h"
1211#endif
1212
1213 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1214 if (RT_SUCCESS(rc))
1215 {
1216 /*
1217 * Register a load done notification to get the MAC address into the slirp
1218 * engine after we loaded a guest state.
1219 */
1220 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1221 AssertRCReturn(rc, rc);
1222
1223 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
1224 if (RT_FAILURE(rc))
1225 {
1226 LogRel(("NAT: Can't create request queue\n"));
1227 return rc;
1228 }
1229
1230 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1231 if (RT_FAILURE(rc))
1232 {
1233 LogRel(("NAT: Can't create request queue\n"));
1234 return rc;
1235 }
1236
1237 rc = RTReqCreateQueue(&pThis->pUrgRecvReqQueue);
1238 if (RT_FAILURE(rc))
1239 {
1240 LogRel(("NAT: Can't create request queue\n"));
1241 return rc;
1242 }
1243
1244 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1245 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1246 AssertRCReturn(rc, rc);
1247
1248 rc = RTSemEventCreate(&pThis->EventRecv);
1249 AssertRCReturn(rc, rc);
1250
1251 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1252 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1253 AssertRCReturn(rc, rc);
1254
1255 rc = RTSemEventCreate(&pThis->EventRecv);
1256 AssertRCReturn(rc, rc);
1257
1258 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1259 AssertRCReturn(rc, rc);
1260
1261 rc = RTCritSectInit(&pThis->DevAccessLock);
1262 AssertRCReturn(rc, rc);
1263
1264 rc = RTCritSectInit(&pThis->XmitLock);
1265 AssertRCReturn(rc, rc);
1266
1267#ifndef RT_OS_WINDOWS
1268 /*
1269 * Create the control pipe.
1270 */
1271 int fds[2];
1272 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1273 {
1274 rc = RTErrConvertFromErrno(errno);
1275 AssertRC(rc);
1276 return rc;
1277 }
1278 pThis->PipeRead = fds[0];
1279 pThis->PipeWrite = fds[1];
1280#else
1281 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1282 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1283 VBOX_WAKEUP_EVENT_INDEX);
1284#endif
1285
1286 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1287 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1288 AssertRC(rc);
1289
1290#ifdef VBOX_WITH_SLIRP_MT
1291 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1292 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1293 AssertRC(rc);
1294#endif
1295
1296 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
1297
1298 /* might return VINF_NAT_DNS */
1299 return rc;
1300 }
1301
1302 /* failure path */
1303 slirp_term(pThis->pNATState);
1304 pThis->pNATState = NULL;
1305 }
1306 else
1307 {
1308 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1309 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1310 }
1311
1312 return rc;
1313}
1314
1315
1316/**
1317 * NAT network transport driver registration record.
1318 */
1319const PDMDRVREG g_DrvNAT =
1320{
1321 /* u32Version */
1322 PDM_DRVREG_VERSION,
1323 /* szName */
1324 "NAT",
1325 /* szRCMod */
1326 "",
1327 /* szR0Mod */
1328 "",
1329 /* pszDescription */
1330 "NAT Network Transport Driver",
1331 /* fFlags */
1332 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1333 /* fClass. */
1334 PDM_DRVREG_CLASS_NETWORK,
1335 /* cMaxInstances */
1336 16,
1337 /* cbInstance */
1338 sizeof(DRVNAT),
1339 /* pfnConstruct */
1340 drvNATConstruct,
1341 /* pfnDestruct */
1342 drvNATDestruct,
1343 /* pfnRelocate */
1344 NULL,
1345 /* pfnIOCtl */
1346 NULL,
1347 /* pfnPowerOn */
1348 drvNATPowerOn,
1349 /* pfnReset */
1350 NULL,
1351 /* pfnSuspend */
1352 NULL,
1353 /* pfnResume */
1354 NULL,
1355 /* pfnAttach */
1356 NULL,
1357 /* pfnDetach */
1358 NULL,
1359 /* pfnPowerOff */
1360 NULL,
1361 /* pfnSoftReset */
1362 NULL,
1363 /* u32EndVersion */
1364 PDM_DRVREG_VERSION
1365};
1366
Note: See TracBrowser for help on using the repository browser.

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