VirtualBox

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

Last change on this file since 33000 was 32139, checked in by vboxsync, 14 years ago

FTM checkpoint setting

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.6 KB
Line 
1/* $Id: DrvNAT.cpp 32139 2010-08-31 12:33:45Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_NAT
23#define __STDC_LIMIT_MACROS
24#define __STDC_CONSTANT_MACROS
25#include "slirp/libslirp.h"
26#include "slirp/ctl.h"
27#include <VBox/pdmdrv.h>
28#include <VBox/pdmnetifs.h>
29#include <VBox/pdmnetinline.h>
30#include <iprt/assert.h>
31#include <iprt/file.h>
32#include <iprt/mem.h>
33#include <iprt/string.h>
34#include <iprt/critsect.h>
35#include <iprt/cidr.h>
36#include <iprt/stream.h>
37#include <iprt/uuid.h>
38
39#include "Builtins.h"
40
41#ifndef RT_OS_WINDOWS
42# include <unistd.h>
43# include <fcntl.h>
44# include <poll.h>
45# include <errno.h>
46#endif
47#ifdef RT_OS_FREEBSD
48# include <netinet/in.h>
49#endif
50#include <iprt/semaphore.h>
51#include <iprt/req.h>
52
53#define COUNTERS_INIT
54#include "counters.h"
55
56
57/*******************************************************************************
58* Defined Constants And Macros *
59*******************************************************************************/
60
61/**
62 * @todo: This is a bad hack to prevent freezing the guest during high network
63 * activity. Windows host only. This needs to be fixed properly.
64 */
65#define VBOX_NAT_DELAY_HACK
66
67#define GET_EXTRADATA(pthis, node, name, rc, type, type_name, var) \
68do { \
69 (rc) = CFGMR3Query ## type((node), name, &(var)); \
70 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
71 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
72 (pthis)->pDrvIns->iInstance); \
73} while (0)
74
75#define GET_ED_STRICT(pthis, node, name, rc, type, type_name, var) \
76do { \
77 (rc) = CFGMR3Query ## type((node), name, &(var)); \
78 if (RT_FAILURE((rc))) \
79 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
80 (pthis)->pDrvIns->iInstance); \
81} while (0)
82
83#define GET_EXTRADATA_N(pthis, node, name, rc, type, type_name, var, var_size) \
84do { \
85 (rc) = CFGMR3Query ## type((node), name, &(var), var_size); \
86 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
87 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
88 (pthis)->pDrvIns->iInstance); \
89} while (0)
90
91#define GET_BOOL(rc, pthis, node, name, var) \
92 GET_EXTRADATA(pthis, node, name, (rc), Bool, bolean, (var))
93#define GET_STRING(rc, pthis, node, name, var, var_size) \
94 GET_EXTRADATA_N(pthis, node, name, (rc), String, string, (var), (var_size))
95#define GET_STRING_ALLOC(rc, pthis, node, name, var) \
96 GET_EXTRADATA(pthis, node, name, (rc), StringAlloc, string, (var))
97#define GET_S32(rc, pthis, node, name, var) \
98 GET_EXTRADATA(pthis, node, name, (rc), S32, int, (var))
99#define GET_S32_STRICT(rc, pthis, node, name, var) \
100 GET_ED_STRICT(pthis, node, name, (rc), S32, int, (var))
101
102
103
104#define DO_GET_IP(rc, node, instance, status, x) \
105do { \
106 char sz##x[32]; \
107 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
108 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
109 (status) = inet_aton(sz ## x, &x); \
110} while (0)
111
112#define GETIP_DEF(rc, node, instance, x, def) \
113do \
114{ \
115 int status = 0; \
116 DO_GET_IP((rc), (node), (instance), status, x); \
117 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
118 x.s_addr = def; \
119} while (0)
120
121/*******************************************************************************
122* Structures and Typedefs *
123*******************************************************************************/
124/**
125 * NAT network transport driver instance data.
126 *
127 * @implements PDMINETWORKUP
128 */
129typedef struct DRVNAT
130{
131 /** The network interface. */
132 PDMINETWORKUP INetworkUp;
133 /** The port we're attached to. */
134 PPDMINETWORKDOWN pIAboveNet;
135 /** The network config of the port we're attached to. */
136 PPDMINETWORKCONFIG pIAboveConfig;
137 /** Pointer to the driver instance. */
138 PPDMDRVINS pDrvIns;
139 /** Link state */
140 PDMNETWORKLINKSTATE enmLinkState;
141 /** NAT state for this instance. */
142 PNATState pNATState;
143 /** TFTP directory prefix. */
144 char *pszTFTPPrefix;
145 /** Boot file name to provide in the DHCP server response. */
146 char *pszBootFile;
147 /** tftp server name to provide in the DHCP server response. */
148 char *pszNextServer;
149 /** Polling thread. */
150 PPDMTHREAD pSlirpThread;
151 /** Queue for NAT-thread-external events. */
152 PRTREQQUEUE pSlirpReqQueue;
153 /** The guest IP for port-forwarding. */
154 uint32_t GuestIP;
155 /** Link state set when the VM is suspended. */
156 PDMNETWORKLINKSTATE enmLinkStateWant;
157
158#ifdef VBOX_WITH_SLIRP_MT
159 PPDMTHREAD pGuestThread;
160#endif
161#ifndef RT_OS_WINDOWS
162 /** The write end of the control pipe. */
163 RTFILE PipeWrite;
164 /** The read end of the control pipe. */
165 RTFILE PipeRead;
166# if HC_ARCH_BITS == 32
167 /** Alignment padding. */
168 uint32_t alignment2;
169# endif
170#else
171 /** for external notification */
172 HANDLE hWakeupEvent;
173#endif
174
175#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
176#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
177#include "counters.h"
178 /** thread delivering packets for receiving by the guest */
179 PPDMTHREAD pRecvThread;
180 /** thread delivering urg packets for receiving by the guest */
181 PPDMTHREAD pUrgRecvThread;
182 /** event to wakeup the guest receive thread */
183 RTSEMEVENT EventRecv;
184 /** event to wakeup the guest urgent receive thread */
185 RTSEMEVENT EventUrgRecv;
186 /** Receive Req queue (deliver packets to the guest) */
187 PRTREQQUEUE pRecvReqQueue;
188 /** Receive Urgent Req queue (deliver packets to the guest). */
189 PRTREQQUEUE pUrgRecvReqQueue;
190
191 /** makes access to device func RecvAvail and Recv atomical. */
192 RTCRITSECT DevAccessLock;
193 /** Number of in-flight urgent packets. */
194 volatile uint32_t cUrgPkts;
195 /** Number of in-flight regular packets. */
196 volatile uint32_t cPkts;
197
198 /** Transmit lock taken by BeginXmit and released by EndXmit. */
199 RTCRITSECT XmitLock;
200} DRVNAT;
201AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
202/** Pointer the NAT driver instance data. */
203typedef DRVNAT *PDRVNAT;
204
205
206/*******************************************************************************
207* Internal Functions *
208*******************************************************************************/
209static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho);
210
211
212static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
213{
214 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
215
216 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
217 return VINF_SUCCESS;
218
219 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
220 {
221 RTReqProcess(pThis->pRecvReqQueue, 0);
222 if (ASMAtomicReadU32(&pThis->cPkts) == 0)
223 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
224 }
225 return VINF_SUCCESS;
226}
227
228
229static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
230{
231 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
232 int rc;
233 rc = RTSemEventSignal(pThis->EventRecv);
234
235 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
236 return VINF_SUCCESS;
237}
238
239static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
240{
241 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
242
243 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
244 return VINF_SUCCESS;
245
246 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
247 {
248 RTReqProcess(pThis->pUrgRecvReqQueue, 0);
249 if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
250 {
251 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
252 AssertRC(rc);
253 }
254 }
255 return VINF_SUCCESS;
256}
257
258static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
259{
260 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
261 int rc = RTSemEventSignal(pThis->EventUrgRecv);
262 AssertRC(rc);
263
264 return VINF_SUCCESS;
265}
266
267static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
268{
269 int rc = RTCritSectEnter(&pThis->DevAccessLock);
270 AssertRC(rc);
271 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
272 if (RT_SUCCESS(rc))
273 {
274 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
275 AssertRC(rc);
276 }
277 else if ( rc != VERR_TIMEOUT
278 && rc != VERR_INTERRUPTED)
279 {
280 AssertRC(rc);
281 }
282
283 rc = RTCritSectLeave(&pThis->DevAccessLock);
284 AssertRC(rc);
285
286 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
287 if (ASMAtomicDecU32(&pThis->cUrgPkts) == 0)
288 {
289 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
290 drvNATNotifyNATThread(pThis, "drvNATUrgRecvWorker");
291 }
292}
293
294
295static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
296{
297 int rc;
298 STAM_PROFILE_START(&pThis->StatNATRecv, a);
299
300 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
301
302 while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
303 {
304 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
305 if ( RT_FAILURE(rc)
306 && ( rc == VERR_TIMEOUT
307 || rc == VERR_INTERRUPTED))
308 goto done_unlocked;
309 }
310
311 rc = RTCritSectEnter(&pThis->DevAccessLock);
312 AssertRC(rc);
313
314 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
315 if (RT_SUCCESS(rc))
316 {
317 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
318 AssertRC(rc);
319 }
320 else if ( rc != VERR_TIMEOUT
321 && rc != VERR_INTERRUPTED)
322 {
323 AssertRC(rc);
324 }
325
326 rc = RTCritSectLeave(&pThis->DevAccessLock);
327 AssertRC(rc);
328
329done_unlocked:
330 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
331 ASMAtomicDecU32(&pThis->cPkts);
332
333 drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
334
335 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
336 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
337}
338
339/**
340 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
341 *
342 * @param pThis Pointer to the NAT instance.
343 * @param pSgBuf The S/G buffer to free.
344 */
345static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
346{
347 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
348 pSgBuf->fFlags = 0;
349 if (pSgBuf->pvAllocator)
350 {
351 Assert(!pSgBuf->pvUser);
352 slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator, NULL);
353 pSgBuf->pvAllocator = NULL;
354 }
355 else if (pSgBuf->pvUser)
356 {
357 RTMemFree(pSgBuf->aSegs[0].pvSeg);
358 pSgBuf->aSegs[0].pvSeg = NULL;
359 RTMemFree(pSgBuf->pvUser);
360 pSgBuf->pvUser = NULL;
361 }
362 RTMemFree(pSgBuf);
363}
364
365/**
366 * Worker function for drvNATSend().
367 *
368 * @param pThis Pointer to the NAT instance.
369 * @param pSgBuf The scatter/gather buffer.
370 * @thread NAT
371 */
372static void drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
373{
374 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
375 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
376 {
377 struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
378 if (m)
379 {
380 /*
381 * A normal frame.
382 */
383 pSgBuf->pvAllocator = NULL;
384 slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
385 }
386 else
387 {
388 /*
389 * GSO frame, need to segment it.
390 */
391 /** @todo Make the NAT engine grok large frames? Could be more efficient... */
392#if 0 /* this is for testing PDMNetGsoCarveSegmentQD. */
393 uint8_t abHdrScratch[256];
394#endif
395 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
396 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
397 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
398 for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
399 {
400 size_t cbSeg;
401 void *pvSeg;
402 m = slirp_ext_m_get(pThis->pNATState, pGso->cbHdrs + pGso->cbMaxSeg, &pvSeg, &cbSeg);
403 if (!m)
404 break;
405
406#if 1
407 uint32_t cbPayload;
408 uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
409 iSeg, cSegs, (uint8_t *)pvSeg, &cbPayload);
410 memcpy((uint8_t *)pvSeg + pGso->cbHdrs, pbFrame + offPayload, cbPayload);
411
412 slirp_input(pThis->pNATState, m, cbPayload + pGso->cbHdrs);
413#else
414 uint32_t cbSegFrame;
415 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
416 iSeg, cSegs, &cbSegFrame);
417 memcpy((uint8_t *)pvSeg, pvSegFrame, cbSegFrame);
418
419 slirp_input(pThis->pNATState, m, cbSegFrame);
420#endif
421 }
422 }
423 }
424 drvNATFreeSgBuf(pThis, pSgBuf);
425
426 /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf sematics. */
427}
428
429/**
430 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
431 */
432static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
433{
434 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
435 int rc = RTCritSectTryEnter(&pThis->XmitLock);
436 if (RT_FAILURE(rc))
437 {
438 /** @todo Kick the worker thread when we have one... */
439 rc = VERR_TRY_AGAIN;
440 }
441 return rc;
442}
443
444/**
445 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
446 */
447static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
448 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
449{
450 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
451 Assert(RTCritSectIsOwner(&pThis->XmitLock));
452
453 /*
454 * Drop the incoming frame if the NAT thread isn't running.
455 */
456 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
457 {
458 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
459 return VERR_NET_NO_NETWORK;
460 }
461
462 /*
463 * Allocate a scatter/gather buffer and an mbuf.
464 */
465 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
466 if (!pSgBuf)
467 return VERR_NO_MEMORY;
468 if (!pGso)
469 {
470 pSgBuf->pvUser = NULL;
471 pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
472 &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
473 if (!pSgBuf->pvAllocator)
474 {
475 RTMemFree(pSgBuf);
476 /** @todo Implement the VERR_TRY_AGAIN semantics. */
477 return VERR_NO_MEMORY;
478 }
479 }
480 else
481 {
482 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
483 pSgBuf->pvAllocator = NULL;
484 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
485 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
486 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
487 {
488 RTMemFree(pSgBuf->aSegs[0].pvSeg);
489 RTMemFree(pSgBuf->pvUser);
490 RTMemFree(pSgBuf);
491 /** @todo Implement the VERR_TRY_AGAIN semantics. */
492 return VERR_NO_MEMORY;
493 }
494 }
495
496 /*
497 * Initialize the S/G buffer and return.
498 */
499 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
500 pSgBuf->cbUsed = 0;
501 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
502 pSgBuf->cSegs = 1;
503
504#if 0 /* poison */
505 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
506#endif
507 *ppSgBuf = pSgBuf;
508 return VINF_SUCCESS;
509}
510
511/**
512 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
513 */
514static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
515{
516 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
517 Assert(RTCritSectIsOwner(&pThis->XmitLock));
518 drvNATFreeSgBuf(pThis, pSgBuf);
519 return VINF_SUCCESS;
520}
521
522/**
523 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
524 */
525static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
526{
527 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
528 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
529 Assert(RTCritSectIsOwner(&pThis->XmitLock));
530
531 int rc;
532 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
533 {
534 /* Set an FTM checkpoint as this operation changes the state permanently. */
535 PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
536
537#ifdef VBOX_WITH_SLIRP_MT
538 PRTREQQUEUE pQueue = (PRTREQQUEUE)slirp_get_queue(pThis->pNATState);
539#else
540 PRTREQQUEUE pQueue = pThis->pSlirpReqQueue;
541#endif
542 rc = RTReqCallEx(pQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
543 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
544 if (RT_SUCCESS(rc))
545 {
546 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
547 return VINF_SUCCESS;
548 }
549
550 rc = VERR_NET_NO_BUFFER_SPACE;
551 }
552 else
553 rc = VERR_NET_DOWN;
554 drvNATFreeSgBuf(pThis, pSgBuf);
555 return rc;
556}
557
558/**
559 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
560 */
561static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
562{
563 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
564 RTCritSectLeave(&pThis->XmitLock);
565}
566
567/**
568 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
569 */
570static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
571{
572 int rc;
573#ifndef RT_OS_WINDOWS
574 /* kick poll() */
575 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
576#else
577 /* kick WSAWaitForMultipleEvents */
578 rc = WSASetEvent(pThis->hWakeupEvent);
579#endif
580 AssertRC(rc);
581}
582
583/**
584 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
585 */
586static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
587{
588 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
589 /* nothing to do */
590}
591
592/**
593 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
594 * @thread "NAT" thread.
595 */
596static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
597{
598 pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
599 switch (enmLinkState)
600 {
601 case PDMNETWORKLINKSTATE_UP:
602 LogRel(("NAT: link up\n"));
603 slirp_link_up(pThis->pNATState);
604 break;
605
606 case PDMNETWORKLINKSTATE_DOWN:
607 case PDMNETWORKLINKSTATE_DOWN_RESUME:
608 LogRel(("NAT: link down\n"));
609 slirp_link_down(pThis->pNATState);
610 break;
611
612 default:
613 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
614 }
615}
616
617/**
618 * Notification on link status changes.
619 *
620 * @param pInterface Pointer to the interface structure containing the called function pointer.
621 * @param enmLinkState The new link state.
622 * @thread EMT
623 */
624static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
625{
626 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
627
628 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
629
630 /* Don't queue new requests when the NAT thread is about to stop.
631 * But the VM could also be paused. So memorize the desired state. */
632 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
633 {
634 pThis->enmLinkStateWant = enmLinkState;
635 return;
636 }
637
638 PRTREQ pReq;
639 int rc = RTReqCallEx(pThis->pSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
640 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
641 if (RT_LIKELY(rc == VERR_TIMEOUT))
642 {
643 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
644 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
645 AssertRC(rc);
646 }
647 else
648 AssertRC(rc);
649 RTReqFree(pReq);
650}
651
652/**
653 * NAT thread handling the slirp stuff.
654 *
655 * The slirp implementation is single-threaded so we execute this enginre in a
656 * dedicated thread. We take care that this thread does not become the
657 * bottleneck: If the guest wants to send, a request is enqueued into the
658 * pSlirpReqQueue and handled asynchronously by this thread. If this thread
659 * wants to deliver packets to the guest, it enqueues a request into
660 * pRecvReqQueue which is later handled by the Recv thread.
661 */
662static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
663{
664 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
665 int nFDs = -1;
666#ifdef RT_OS_WINDOWS
667 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
668 unsigned int cBreak = 0;
669#else /* RT_OS_WINDOWS */
670 unsigned int cPollNegRet = 0;
671#endif /* !RT_OS_WINDOWS */
672
673 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
674
675 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
676 return VINF_SUCCESS;
677
678 if (pThis->enmLinkStateWant != pThis->enmLinkState)
679 drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
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 int MTU = 1500;
1130 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1131 int i32AliasMode = 0;
1132 int i32MainAliasMode = 0;
1133 GET_S32(rc, pThis, pCfg, "AliasMode", i32MainAliasMode);
1134
1135 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1136 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1137 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1138 /*
1139 * Query the network port interface.
1140 */
1141 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1142 if (!pThis->pIAboveNet)
1143 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1144 N_("Configuration error: the above device/driver didn't "
1145 "export the network port interface"));
1146 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1147 if (!pThis->pIAboveConfig)
1148 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1149 N_("Configuration error: the above device/driver didn't "
1150 "export the network config interface"));
1151
1152 /* Generate a network address for this network card. */
1153 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1154 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1155 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1156 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1157 "missing network"),
1158 pDrvIns->iInstance, szNetwork);
1159
1160 RTIPV4ADDR Network;
1161 RTIPV4ADDR Netmask;
1162 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1163 if (RT_FAILURE(rc))
1164 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1165 "network '%s' describes not a valid IPv4 network"),
1166 pDrvIns->iInstance, szNetwork);
1167
1168 /*
1169 * Initialize slirp.
1170 */
1171 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask,
1172 fPassDomain, !!fUseHostResolver, i32AliasMode, pThis);
1173 if (RT_SUCCESS(rc))
1174 {
1175 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1176 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1177 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1178 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1179 slirp_set_mtu(pThis->pNATState, MTU);
1180 char *pszBindIP = NULL;
1181 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1182 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1183 if (rc != 0)
1184 LogRel(("NAT: value of BindIP has been ignored\n"));
1185
1186 if(pszBindIP != NULL)
1187 MMR3HeapFree(pszBindIP);
1188#define SLIRP_SET_TUNING_VALUE(name, setter) \
1189 do \
1190 { \
1191 int len = 0; \
1192 rc = CFGMR3QueryS32(pCfg, name, &len); \
1193 if (RT_SUCCESS(rc)) \
1194 setter(pThis->pNATState, len); \
1195 } while(0)
1196
1197 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1198 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1199 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1200 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1201
1202 slirp_register_statistics(pThis->pNATState, pDrvIns);
1203#ifdef VBOX_WITH_STATISTICS
1204# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1205# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1206# include "counters.h"
1207#endif
1208
1209 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1210 if (RT_SUCCESS(rc))
1211 {
1212 /*
1213 * Register a load done notification to get the MAC address into the slirp
1214 * engine after we loaded a guest state.
1215 */
1216 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1217 AssertRCReturn(rc, rc);
1218
1219 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
1220 if (RT_FAILURE(rc))
1221 {
1222 LogRel(("NAT: Can't create request queue\n"));
1223 return rc;
1224 }
1225
1226 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1227 if (RT_FAILURE(rc))
1228 {
1229 LogRel(("NAT: Can't create request queue\n"));
1230 return rc;
1231 }
1232
1233 rc = RTReqCreateQueue(&pThis->pUrgRecvReqQueue);
1234 if (RT_FAILURE(rc))
1235 {
1236 LogRel(("NAT: Can't create request queue\n"));
1237 return rc;
1238 }
1239
1240 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1241 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1242 AssertRCReturn(rc, rc);
1243
1244 rc = RTSemEventCreate(&pThis->EventRecv);
1245 AssertRCReturn(rc, rc);
1246
1247 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1248 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1249 AssertRCReturn(rc, rc);
1250
1251 rc = RTSemEventCreate(&pThis->EventRecv);
1252 AssertRCReturn(rc, rc);
1253
1254 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1255 AssertRCReturn(rc, rc);
1256
1257 rc = RTCritSectInit(&pThis->DevAccessLock);
1258 AssertRCReturn(rc, rc);
1259
1260 rc = RTCritSectInit(&pThis->XmitLock);
1261 AssertRCReturn(rc, rc);
1262
1263#ifndef RT_OS_WINDOWS
1264 /*
1265 * Create the control pipe.
1266 */
1267 int fds[2];
1268 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1269 {
1270 rc = RTErrConvertFromErrno(errno);
1271 AssertRC(rc);
1272 return rc;
1273 }
1274 pThis->PipeRead = fds[0];
1275 pThis->PipeWrite = fds[1];
1276#else
1277 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1278 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1279 VBOX_WAKEUP_EVENT_INDEX);
1280#endif
1281
1282 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1283 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1284 AssertRC(rc);
1285
1286#ifdef VBOX_WITH_SLIRP_MT
1287 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1288 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1289 AssertRC(rc);
1290#endif
1291
1292 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1293
1294 /* might return VINF_NAT_DNS */
1295 return rc;
1296 }
1297
1298 /* failure path */
1299 slirp_term(pThis->pNATState);
1300 pThis->pNATState = NULL;
1301 }
1302 else
1303 {
1304 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1305 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1306 }
1307
1308 return rc;
1309}
1310
1311
1312/**
1313 * NAT network transport driver registration record.
1314 */
1315const PDMDRVREG g_DrvNAT =
1316{
1317 /* u32Version */
1318 PDM_DRVREG_VERSION,
1319 /* szName */
1320 "NAT",
1321 /* szRCMod */
1322 "",
1323 /* szR0Mod */
1324 "",
1325 /* pszDescription */
1326 "NAT Network Transport Driver",
1327 /* fFlags */
1328 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1329 /* fClass. */
1330 PDM_DRVREG_CLASS_NETWORK,
1331 /* cMaxInstances */
1332 16,
1333 /* cbInstance */
1334 sizeof(DRVNAT),
1335 /* pfnConstruct */
1336 drvNATConstruct,
1337 /* pfnDestruct */
1338 drvNATDestruct,
1339 /* pfnRelocate */
1340 NULL,
1341 /* pfnIOCtl */
1342 NULL,
1343 /* pfnPowerOn */
1344 drvNATPowerOn,
1345 /* pfnReset */
1346 NULL,
1347 /* pfnSuspend */
1348 NULL,
1349 /* pfnResume */
1350 NULL,
1351 /* pfnAttach */
1352 NULL,
1353 /* pfnDetach */
1354 NULL,
1355 /* pfnPowerOff */
1356 NULL,
1357 /* pfnSoftReset */
1358 NULL,
1359 /* u32EndVersion */
1360 PDM_DRVREG_VERSION
1361};
1362
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use