VirtualBox

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

Last change on this file since 37636 was 37596, checked in by vboxsync, 13 years ago

*: RTFILE becomes a pointer, RTFileOpen++ expands it's flags paramter from uint32_t to uint64_t.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.4 KB
Line 
1/* $Id: DrvNAT.cpp 37596 2011-06-22 19:30:06Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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/vmm/pdmdrv.h>
28#include <VBox/vmm/pdmnetifs.h>
29#include <VBox/vmm/pdmnetinline.h>
30
31#include <iprt/assert.h>
32#include <iprt/critsect.h>
33#include <iprt/cidr.h>
34#include <iprt/file.h>
35#include <iprt/mem.h>
36#include <iprt/pipe.h>
37#include <iprt/string.h>
38#include <iprt/stream.h>
39#include <iprt/uuid.h>
40
41#include "VBoxDD.h"
42
43#ifndef RT_OS_WINDOWS
44# include <unistd.h>
45# include <fcntl.h>
46# include <poll.h>
47# include <errno.h>
48#endif
49#ifdef RT_OS_FREEBSD
50# include <netinet/in.h>
51#endif
52#include <iprt/semaphore.h>
53#include <iprt/req.h>
54
55#define COUNTERS_INIT
56#include "counters.h"
57
58
59/*******************************************************************************
60* Defined Constants And Macros *
61*******************************************************************************/
62
63/**
64 * @todo: This is a bad hack to prevent freezing the guest during high network
65 * activity. Windows host only. This needs to be fixed properly.
66 */
67#define VBOX_NAT_DELAY_HACK
68
69#define GET_EXTRADATA(pthis, node, name, rc, type, type_name, var) \
70do { \
71 (rc) = CFGMR3Query ## type((node), name, &(var)); \
72 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
73 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
74 (pthis)->pDrvIns->iInstance); \
75} while (0)
76
77#define GET_ED_STRICT(pthis, node, name, rc, type, type_name, var) \
78do { \
79 (rc) = CFGMR3Query ## type((node), name, &(var)); \
80 if (RT_FAILURE((rc))) \
81 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
82 (pthis)->pDrvIns->iInstance); \
83} while (0)
84
85#define GET_EXTRADATA_N(pthis, node, name, rc, type, type_name, var, var_size) \
86do { \
87 (rc) = CFGMR3Query ## type((node), name, &(var), var_size); \
88 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
89 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
90 (pthis)->pDrvIns->iInstance); \
91} while (0)
92
93#define GET_BOOL(rc, pthis, node, name, var) \
94 GET_EXTRADATA(pthis, node, name, (rc), Bool, bolean, (var))
95#define GET_STRING(rc, pthis, node, name, var, var_size) \
96 GET_EXTRADATA_N(pthis, node, name, (rc), String, string, (var), (var_size))
97#define GET_STRING_ALLOC(rc, pthis, node, name, var) \
98 GET_EXTRADATA(pthis, node, name, (rc), StringAlloc, string, (var))
99#define GET_S32(rc, pthis, node, name, var) \
100 GET_EXTRADATA(pthis, node, name, (rc), S32, int, (var))
101#define GET_S32_STRICT(rc, pthis, node, name, var) \
102 GET_ED_STRICT(pthis, node, name, (rc), S32, int, (var))
103
104
105
106#define DO_GET_IP(rc, node, instance, status, x) \
107do { \
108 char sz##x[32]; \
109 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
110 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
111 (status) = inet_aton(sz ## x, &x); \
112} while (0)
113
114#define GETIP_DEF(rc, node, instance, x, def) \
115do \
116{ \
117 int status = 0; \
118 DO_GET_IP((rc), (node), (instance), status, x); \
119 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
120 x.s_addr = def; \
121} while (0)
122
123/*******************************************************************************
124* Structures and Typedefs *
125*******************************************************************************/
126/**
127 * NAT network transport driver instance data.
128 *
129 * @implements PDMINETWORKUP
130 */
131typedef struct DRVNAT
132{
133 /** The network interface. */
134 PDMINETWORKUP INetworkUp;
135 /** The network NAT Engine configureation. */
136 PDMINETWORKNATCONFIG INetworkNATCfg;
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 /** Link state set when the VM is suspended. */
160 PDMNETWORKLINKSTATE enmLinkStateWant;
161
162#ifdef VBOX_WITH_SLIRP_MT
163 PPDMTHREAD pGuestThread;
164#endif
165#ifndef RT_OS_WINDOWS
166 /** The write end of the control pipe. */
167 RTPIPE hPipeWrite;
168 /** The read end of the control pipe. */
169 RTPIPE hPipeRead;
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 semantics. */
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 return VERR_TRY_AGAIN;
477 }
478 }
479 else
480 {
481 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
482 pSgBuf->pvAllocator = NULL;
483 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
484 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
485 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
486 {
487 RTMemFree(pSgBuf->aSegs[0].pvSeg);
488 RTMemFree(pSgBuf->pvUser);
489 RTMemFree(pSgBuf);
490 return VERR_TRY_AGAIN;
491 }
492 }
493
494 /*
495 * Initialize the S/G buffer and return.
496 */
497 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
498 pSgBuf->cbUsed = 0;
499 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
500 pSgBuf->cSegs = 1;
501
502#if 0 /* poison */
503 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
504#endif
505 *ppSgBuf = pSgBuf;
506 return VINF_SUCCESS;
507}
508
509/**
510 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
511 */
512static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
513{
514 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
515 Assert(RTCritSectIsOwner(&pThis->XmitLock));
516 drvNATFreeSgBuf(pThis, pSgBuf);
517 return VINF_SUCCESS;
518}
519
520/**
521 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
522 */
523static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
524{
525 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
526 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
527 Assert(RTCritSectIsOwner(&pThis->XmitLock));
528
529 int rc;
530 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
531 {
532 /* Set an FTM checkpoint as this operation changes the state permanently. */
533 PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
534
535#ifdef VBOX_WITH_SLIRP_MT
536 PRTREQQUEUE pQueue = (PRTREQQUEUE)slirp_get_queue(pThis->pNATState);
537#else
538 PRTREQQUEUE pQueue = pThis->pSlirpReqQueue;
539#endif
540 rc = RTReqCallEx(pQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
541 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
542 if (RT_SUCCESS(rc))
543 {
544 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
545 return VINF_SUCCESS;
546 }
547
548 rc = VERR_NET_NO_BUFFER_SPACE;
549 }
550 else
551 rc = VERR_NET_DOWN;
552 drvNATFreeSgBuf(pThis, pSgBuf);
553 return rc;
554}
555
556/**
557 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
558 */
559static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
560{
561 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
562 RTCritSectLeave(&pThis->XmitLock);
563}
564
565/**
566 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
567 */
568static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
569{
570 int rc;
571#ifndef RT_OS_WINDOWS
572 /* kick poll() */
573 size_t cbIgnored;
574 rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
575#else
576 /* kick WSAWaitForMultipleEvents */
577 rc = WSASetEvent(pThis->hWakeupEvent);
578#endif
579 AssertRC(rc);
580}
581
582/**
583 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
584 */
585static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
586{
587 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
588 /* nothing to do */
589}
590
591/**
592 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
593 * @thread "NAT" thread.
594 */
595static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
596{
597 pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
598 switch (enmLinkState)
599 {
600 case PDMNETWORKLINKSTATE_UP:
601 LogRel(("NAT: link up\n"));
602 slirp_link_up(pThis->pNATState);
603 break;
604
605 case PDMNETWORKLINKSTATE_DOWN:
606 case PDMNETWORKLINKSTATE_DOWN_RESUME:
607 LogRel(("NAT: link down\n"));
608 slirp_link_down(pThis->pNATState);
609 break;
610
611 default:
612 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
613 }
614}
615
616/**
617 * Notification on link status changes.
618 *
619 * @param pInterface Pointer to the interface structure containing the called function pointer.
620 * @param enmLinkState The new link state.
621 * @thread EMT
622 */
623static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
624{
625 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
626
627 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
628
629 /* Don't queue new requests when the NAT thread is about to stop.
630 * But the VM could also be paused. So memorize the desired state. */
631 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
632 {
633 pThis->enmLinkStateWant = enmLinkState;
634 return;
635 }
636
637 PRTREQ pReq;
638 int rc = RTReqCallEx(pThis->pSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
639 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
640 if (RT_LIKELY(rc == VERR_TIMEOUT))
641 {
642 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
643 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
644 AssertRC(rc);
645 }
646 else
647 AssertRC(rc);
648 RTReqFree(pReq);
649}
650
651static void drvNATNotifyApplyPortForwardCommand(PDRVNAT pThis, bool fRemove,
652 bool fUdp, const char *pHostIp,
653 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
654{
655 RTMAC Mac;
656 RT_ZERO(Mac); /* can't get MAC here */
657 if (pThis->pIAboveConfig)
658 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
659
660 struct in_addr guestIp, hostIp;
661
662 if ( pHostIp == NULL
663 || inet_aton(pHostIp, &hostIp) == 0)
664 hostIp.s_addr = INADDR_ANY;
665
666 if ( pGuestIp == NULL
667 || inet_aton(pGuestIp, &guestIp) == 0)
668 guestIp.s_addr = pThis->GuestIP;
669
670 if (fRemove)
671 slirp_remove_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
672 else
673 slirp_add_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort, Mac.au8);
674}
675
676DECLCALLBACK(int) drvNATNetworkNatConfig_RedirectRuleCommand(PPDMINETWORKNATCONFIG pInterface, bool fRemove,
677 bool fUdp, const char *pHostIp,
678 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
679{
680 LogFlowFunc(("fRemove=%d, fUdp=%d, pHostIp=%s, u16HostPort=%u, pGuestIp=%s, u16GuestPort=%u\n",
681 RT_BOOL(fRemove), RT_BOOL(fUdp), pHostIp, u16HostPort, pGuestIp,
682 u16GuestPort));
683 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
684 PRTREQ pReq;
685 int rc = RTReqCallEx(pThis->pSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
686 (PFNRT)drvNATNotifyApplyPortForwardCommand, 7, pThis, fRemove,
687 fUdp, pHostIp, u16HostPort, pGuestIp, u16GuestPort);
688 if (RT_LIKELY(rc == VERR_TIMEOUT))
689 {
690 drvNATNotifyNATThread(pThis, "drvNATNetworkNatConfig_RedirectRuleCommand");
691 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
692 AssertRC(rc);
693 }
694 else
695 AssertRC(rc);
696
697 RTReqFree(pReq);
698 port_forwarding_done:
699 return rc;
700}
701
702/**
703 * NAT thread handling the slirp stuff.
704 *
705 * The slirp implementation is single-threaded so we execute this enginre in a
706 * dedicated thread. We take care that this thread does not become the
707 * bottleneck: If the guest wants to send, a request is enqueued into the
708 * pSlirpReqQueue and handled asynchronously by this thread. If this thread
709 * wants to deliver packets to the guest, it enqueues a request into
710 * pRecvReqQueue which is later handled by the Recv thread.
711 */
712static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
713{
714 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
715 int nFDs = -1;
716#ifdef RT_OS_WINDOWS
717 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
718 unsigned int cBreak = 0;
719#else /* RT_OS_WINDOWS */
720 unsigned int cPollNegRet = 0;
721#endif /* !RT_OS_WINDOWS */
722
723 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
724
725 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
726 return VINF_SUCCESS;
727
728 if (pThis->enmLinkStateWant != pThis->enmLinkState)
729 drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
730
731 /*
732 * Polling loop.
733 */
734 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
735 {
736 /*
737 * To prevent concurrent execution of sending/receiving threads
738 */
739#ifndef RT_OS_WINDOWS
740 nFDs = slirp_get_nsock(pThis->pNATState);
741 /* allocation for all sockets + Management pipe */
742 struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
743 if (polls == NULL)
744 return VERR_NO_MEMORY;
745
746 /* don't pass the management pipe */
747 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
748
749 polls[0].fd = RTPipeToNative(pThis->hPipeRead);
750 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
751 polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
752 polls[0].revents = 0;
753
754 int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
755 if (cChangedFDs < 0)
756 {
757 if (errno == EINTR)
758 {
759 Log2(("NAT: signal was caught while sleep on poll\n"));
760 /* No error, just process all outstanding requests but don't wait */
761 cChangedFDs = 0;
762 }
763 else if (cPollNegRet++ > 128)
764 {
765 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
766 cPollNegRet = 0;
767 }
768 }
769
770 if (cChangedFDs >= 0)
771 {
772 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
773 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
774 {
775 /* drain the pipe
776 *
777 * Note! drvNATSend decoupled so we don't know how many times
778 * device's thread sends before we've entered multiplex,
779 * so to avoid false alarm drain pipe here to the very end
780 *
781 * @todo: Probably we should counter drvNATSend to count how
782 * deep pipe has been filed before drain.
783 *
784 */
785 /** @todo XXX: Make it reading exactly we need to drain the
786 * pipe.*/
787 char ch;
788 size_t cbRead;
789 RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
790 }
791 }
792 /* process _all_ outstanding requests but don't wait */
793 RTReqProcess(pThis->pSlirpReqQueue, 0);
794 RTMemFree(polls);
795
796#else /* RT_OS_WINDOWS */
797 nFDs = -1;
798 slirp_select_fill(pThis->pNATState, &nFDs);
799 DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
800 slirp_get_timeout_ms(pThis->pNATState),
801 FALSE);
802 if ( (dwEvent < WSA_WAIT_EVENT_0 || dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
803 && dwEvent != WSA_WAIT_TIMEOUT)
804 {
805 int error = WSAGetLastError();
806 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
807 RTAssertPanic();
808 }
809
810 if (dwEvent == WSA_WAIT_TIMEOUT)
811 {
812 /* only check for slow/fast timers */
813 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
814 continue;
815 }
816 /* poll the sockets in any case */
817 Log2(("%s: poll\n", __FUNCTION__));
818 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(dwEvent == WSA_WAIT_EVENT_0));
819 /* process _all_ outstanding requests but don't wait */
820 RTReqProcess(pThis->pSlirpReqQueue, 0);
821# ifdef VBOX_NAT_DELAY_HACK
822 if (cBreak++ > 128)
823 {
824 cBreak = 0;
825 RTThreadSleep(2);
826 }
827# endif
828#endif /* RT_OS_WINDOWS */
829 }
830
831 return VINF_SUCCESS;
832}
833
834
835/**
836 * Unblock the send thread so it can respond to a state change.
837 *
838 * @returns VBox status code.
839 * @param pDevIns The pcnet device instance.
840 * @param pThread The send thread.
841 */
842static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
843{
844 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
845
846 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
847 return VINF_SUCCESS;
848}
849
850#ifdef VBOX_WITH_SLIRP_MT
851
852static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
853{
854 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
855
856 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
857 return VINF_SUCCESS;
858
859 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
860 slirp_process_queue(pThis->pNATState);
861
862 return VINF_SUCCESS;
863}
864
865
866static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
867{
868 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
869
870 return VINF_SUCCESS;
871}
872
873#endif /* VBOX_WITH_SLIRP_MT */
874
875/**
876 * Function called by slirp to check if it's possible to feed incoming data to the network port.
877 * @returns 1 if possible.
878 * @returns 0 if not possible.
879 */
880int slirp_can_output(void *pvUser)
881{
882 return 1;
883}
884
885void slirp_push_recv_thread(void *pvUser)
886{
887 PDRVNAT pThis = (PDRVNAT)pvUser;
888 Assert(pThis);
889 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
890}
891
892void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
893{
894 PDRVNAT pThis = (PDRVNAT)pvUser;
895 Assert(pThis);
896
897 PRTREQ pReq = NULL;
898
899 /* don't queue new requests when the NAT thread is about to stop */
900 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
901 return;
902
903 ASMAtomicIncU32(&pThis->cUrgPkts);
904 int rc = RTReqCallEx(pThis->pUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
905 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
906 AssertRC(rc);
907 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
908}
909
910/**
911 * Function called by slirp to wake up device after VERR_TRY_AGAIN
912 */
913void slirp_output_pending(void *pvUser)
914{
915 PDRVNAT pThis = (PDRVNAT)pvUser;
916 Assert(pThis);
917 pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
918}
919
920/**
921 * Function called by slirp to feed incoming data to the NIC.
922 */
923void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
924{
925 PDRVNAT pThis = (PDRVNAT)pvUser;
926 Assert(pThis);
927
928 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
929 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
930
931 PRTREQ pReq = NULL;
932
933 /* don't queue new requests when the NAT thread is about to stop */
934 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
935 return;
936
937 ASMAtomicIncU32(&pThis->cPkts);
938 int rc = RTReqCallEx(pThis->pRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
939 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
940 AssertRC(rc);
941 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
942 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
943}
944
945
946/**
947 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
948 */
949static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
950{
951 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
952 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
953
954 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
955 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
956 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKNATCONFIG, &pThis->INetworkNATCfg);
957 return NULL;
958}
959
960
961/**
962 * Get the MAC address into the slirp stack.
963 *
964 * Called by drvNATLoadDone and drvNATPowerOn.
965 */
966static void drvNATSetMac(PDRVNAT pThis)
967{
968 if (pThis->pIAboveConfig)
969 {
970 RTMAC Mac;
971 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
972 /* Re-activate the port forwarding. If */
973 slirp_set_ethaddr_and_activate_port_forwarding(pThis->pNATState, Mac.au8, pThis->GuestIP);
974 }
975}
976
977
978/**
979 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
980 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
981 * (usually done during guest boot).
982 */
983static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
984{
985 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
986 drvNATSetMac(pThis);
987 return VINF_SUCCESS;
988}
989
990
991/**
992 * Some guests might not use DHCP to retrieve an IP but use a static IP.
993 */
994static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
995{
996 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
997 drvNATSetMac(pThis);
998}
999
1000
1001/**
1002 * Info handler.
1003 */
1004static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1005{
1006 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1007 slirp_info(pThis->pNATState, pHlp, pszArgs);
1008}
1009
1010
1011/**
1012 * Sets up the redirectors.
1013 *
1014 * @returns VBox status code.
1015 * @param pCfg The configuration handle.
1016 */
1017static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network)
1018{
1019 RTMAC Mac;
1020 RT_ZERO(Mac); /* can't get MAC here */
1021
1022 /*
1023 * Enumerate redirections.
1024 */
1025 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
1026 {
1027 /*
1028 * Validate the port forwarding config.
1029 */
1030 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
1031 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1032 N_("Unknown configuration in port forwarding"));
1033
1034 /* protocol type */
1035 bool fUDP;
1036 char szProtocol[32];
1037 int rc;
1038 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
1039 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1040 {
1041 fUDP = false;
1042 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
1043 }
1044 else if (RT_SUCCESS(rc))
1045 {
1046 if (!RTStrICmp(szProtocol, "TCP"))
1047 fUDP = false;
1048 else if (!RTStrICmp(szProtocol, "UDP"))
1049 fUDP = true;
1050 else
1051 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1052 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
1053 iInstance, szProtocol);
1054 }
1055 else
1056 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS,
1057 N_("NAT#%d: configuration query for \"Protocol\" failed"),
1058 iInstance);
1059 /* host port */
1060 int32_t iHostPort;
1061 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
1062
1063 /* guest port */
1064 int32_t iGuestPort;
1065 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
1066
1067 /* guest address */
1068 struct in_addr GuestIP;
1069 /* @todo (vvl) use CTL_* */
1070 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
1071
1072 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
1073 * is not documented. Without */
1074 if (pThis->GuestIP == INADDR_ANY)
1075 pThis->GuestIP = GuestIP.s_addr;
1076
1077 /*
1078 * Call slirp about it.
1079 */
1080 struct in_addr BindIP;
1081 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
1082 if (slirp_add_redirect(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
1083 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1084 N_("NAT#%d: configuration error: failed to set up "
1085 "redirection of %d to %d. Probably a conflict with "
1086 "existing services or other rules"), iInstance, iHostPort,
1087 iGuestPort);
1088 } /* for each redir rule */
1089
1090 return VINF_SUCCESS;
1091}
1092
1093
1094/**
1095 * Destruct a driver instance.
1096 *
1097 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1098 * resources can be freed correctly.
1099 *
1100 * @param pDrvIns The driver instance data.
1101 */
1102static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1103{
1104 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1105 LogFlow(("drvNATDestruct:\n"));
1106 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1107
1108 if (pThis->pNATState)
1109 {
1110 slirp_term(pThis->pNATState);
1111 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1112#ifdef VBOX_WITH_STATISTICS
1113# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1114# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1115# include "counters.h"
1116#endif
1117 pThis->pNATState = NULL;
1118 }
1119
1120 RTReqDestroyQueue(pThis->pSlirpReqQueue);
1121 pThis->pSlirpReqQueue = NULL;
1122
1123 RTReqDestroyQueue(pThis->pUrgRecvReqQueue);
1124 pThis->pUrgRecvReqQueue = NULL;
1125
1126 RTSemEventDestroy(pThis->EventRecv);
1127 pThis->EventRecv = NIL_RTSEMEVENT;
1128
1129 RTSemEventDestroy(pThis->EventUrgRecv);
1130 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1131
1132 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1133 RTCritSectDelete(&pThis->DevAccessLock);
1134
1135 if (RTCritSectIsInitialized(&pThis->XmitLock))
1136 RTCritSectDelete(&pThis->XmitLock);
1137}
1138
1139
1140/**
1141 * Construct a NAT network transport driver instance.
1142 *
1143 * @copydoc FNPDMDRVCONSTRUCT
1144 */
1145static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1146{
1147 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1148 LogFlow(("drvNATConstruct:\n"));
1149 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1150
1151 /*
1152 * Validate the config.
1153 */
1154 if (!CFGMR3AreValuesValid(pCfg,
1155 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1156 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1157 "SlirpMTU\0AliasMode\0"
1158 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"))
1159 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1160 N_("Unknown NAT configuration option, only supports PassDomain,"
1161 " TFTPPrefix, BootFile and Network"));
1162
1163 /*
1164 * Init the static parts.
1165 */
1166 pThis->pDrvIns = pDrvIns;
1167 pThis->pNATState = NULL;
1168 pThis->pszTFTPPrefix = NULL;
1169 pThis->pszBootFile = NULL;
1170 pThis->pszNextServer = NULL;
1171 pThis->pSlirpReqQueue = NULL;
1172 pThis->pUrgRecvReqQueue = NULL;
1173 pThis->EventRecv = NIL_RTSEMEVENT;
1174 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1175
1176 /* IBase */
1177 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1178
1179 /* INetwork */
1180 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1181 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1182 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1183 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1184 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1185 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1186 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1187
1188 /* NAT engine configuration */
1189 pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfig_RedirectRuleCommand;
1190
1191 /*
1192 * Get the configuration settings.
1193 */
1194 int rc;
1195 bool fPassDomain = true;
1196 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1197
1198 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1199 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1200 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1201
1202 int fDNSProxy = 0;
1203 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1204 int fUseHostResolver = 0;
1205 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1206 int MTU = 1500;
1207 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1208 int i32AliasMode = 0;
1209 int i32MainAliasMode = 0;
1210 GET_S32(rc, pThis, pCfg, "AliasMode", i32MainAliasMode);
1211
1212 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1213 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1214 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1215 /*
1216 * Query the network port interface.
1217 */
1218 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1219 if (!pThis->pIAboveNet)
1220 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1221 N_("Configuration error: the above device/driver didn't "
1222 "export the network port interface"));
1223 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1224 if (!pThis->pIAboveConfig)
1225 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1226 N_("Configuration error: the above device/driver didn't "
1227 "export the network config interface"));
1228
1229 /* Generate a network address for this network card. */
1230 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1231 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1232 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1233 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1234 "missing network"),
1235 pDrvIns->iInstance, szNetwork);
1236
1237 RTIPV4ADDR Network;
1238 RTIPV4ADDR Netmask;
1239 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1240 if (RT_FAILURE(rc))
1241 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1242 "network '%s' describes not a valid IPv4 network"),
1243 pDrvIns->iInstance, szNetwork);
1244
1245 /*
1246 * Initialize slirp.
1247 */
1248 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask,
1249 fPassDomain, !!fUseHostResolver, i32AliasMode, pThis);
1250 if (RT_SUCCESS(rc))
1251 {
1252 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1253 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1254 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1255 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1256 slirp_set_mtu(pThis->pNATState, MTU);
1257 char *pszBindIP = NULL;
1258 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1259 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1260 if (rc != 0)
1261 LogRel(("NAT: value of BindIP has been ignored\n"));
1262
1263 if(pszBindIP != NULL)
1264 MMR3HeapFree(pszBindIP);
1265#define SLIRP_SET_TUNING_VALUE(name, setter) \
1266 do \
1267 { \
1268 int len = 0; \
1269 rc = CFGMR3QueryS32(pCfg, name, &len); \
1270 if (RT_SUCCESS(rc)) \
1271 setter(pThis->pNATState, len); \
1272 } while(0)
1273
1274 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1275 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1276 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1277 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1278
1279 slirp_register_statistics(pThis->pNATState, pDrvIns);
1280#ifdef VBOX_WITH_STATISTICS
1281# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1282# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1283# include "counters.h"
1284#endif
1285
1286 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1287 if (RT_SUCCESS(rc))
1288 {
1289 /*
1290 * Register a load done notification to get the MAC address into the slirp
1291 * engine after we loaded a guest state.
1292 */
1293 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1294 AssertRCReturn(rc, rc);
1295
1296 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
1297 if (RT_FAILURE(rc))
1298 {
1299 LogRel(("NAT: Can't create request queue\n"));
1300 return rc;
1301 }
1302
1303 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1304 if (RT_FAILURE(rc))
1305 {
1306 LogRel(("NAT: Can't create request queue\n"));
1307 return rc;
1308 }
1309
1310 rc = RTReqCreateQueue(&pThis->pUrgRecvReqQueue);
1311 if (RT_FAILURE(rc))
1312 {
1313 LogRel(("NAT: Can't create request queue\n"));
1314 return rc;
1315 }
1316
1317 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1318 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1319 AssertRCReturn(rc, rc);
1320
1321 rc = RTSemEventCreate(&pThis->EventRecv);
1322 AssertRCReturn(rc, rc);
1323
1324 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1325 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1326 AssertRCReturn(rc, rc);
1327
1328 rc = RTSemEventCreate(&pThis->EventRecv);
1329 AssertRCReturn(rc, rc);
1330
1331 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1332 AssertRCReturn(rc, rc);
1333
1334 rc = RTCritSectInit(&pThis->DevAccessLock);
1335 AssertRCReturn(rc, rc);
1336
1337 rc = RTCritSectInit(&pThis->XmitLock);
1338 AssertRCReturn(rc, rc);
1339
1340 char szTmp[128];
1341 RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
1342 PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
1343
1344#ifndef RT_OS_WINDOWS
1345 /*
1346 * Create the control pipe.
1347 */
1348 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
1349 AssertRCReturn(rc, rc);
1350#else
1351 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1352 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1353 VBOX_WAKEUP_EVENT_INDEX);
1354#endif
1355
1356 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1357 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1358 AssertRCReturn(rc, rc);
1359
1360#ifdef VBOX_WITH_SLIRP_MT
1361 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1362 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1363 AssertRCReturn(rc, rc);
1364#endif
1365
1366 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1367
1368 /* might return VINF_NAT_DNS */
1369 return rc;
1370 }
1371
1372 /* failure path */
1373 slirp_term(pThis->pNATState);
1374 pThis->pNATState = NULL;
1375 }
1376 else
1377 {
1378 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1379 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1380 }
1381
1382 return rc;
1383}
1384
1385
1386/**
1387 * NAT network transport driver registration record.
1388 */
1389const PDMDRVREG g_DrvNAT =
1390{
1391 /* u32Version */
1392 PDM_DRVREG_VERSION,
1393 /* szName */
1394 "NAT",
1395 /* szRCMod */
1396 "",
1397 /* szR0Mod */
1398 "",
1399 /* pszDescription */
1400 "NAT Network Transport Driver",
1401 /* fFlags */
1402 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1403 /* fClass. */
1404 PDM_DRVREG_CLASS_NETWORK,
1405 /* cMaxInstances */
1406 16,
1407 /* cbInstance */
1408 sizeof(DRVNAT),
1409 /* pfnConstruct */
1410 drvNATConstruct,
1411 /* pfnDestruct */
1412 drvNATDestruct,
1413 /* pfnRelocate */
1414 NULL,
1415 /* pfnIOCtl */
1416 NULL,
1417 /* pfnPowerOn */
1418 drvNATPowerOn,
1419 /* pfnReset */
1420 NULL,
1421 /* pfnSuspend */
1422 NULL,
1423 /* pfnResume */
1424 NULL,
1425 /* pfnAttach */
1426 NULL,
1427 /* pfnDetach */
1428 NULL,
1429 /* pfnPowerOff */
1430 NULL,
1431 /* pfnSoftReset */
1432 NULL,
1433 /* u32EndVersion */
1434 PDM_DRVREG_VERSION
1435};
1436
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