VirtualBox

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

Last change on this file since 44889 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.3 KB
Line 
1/* $Id: DrvNAT.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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 RTREQQUEUE hSlirpReqQueue;
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#ifndef RT_OS_WINDOWS
163 /** The write end of the control pipe. */
164 RTPIPE hPipeWrite;
165 /** The read end of the control pipe. */
166 RTPIPE hPipeRead;
167#else
168 /** for external notification */
169 HANDLE hWakeupEvent;
170#endif
171
172#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
173#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
174#include "counters.h"
175 /** thread delivering packets for receiving by the guest */
176 PPDMTHREAD pRecvThread;
177 /** thread delivering urg packets for receiving by the guest */
178 PPDMTHREAD pUrgRecvThread;
179 /** event to wakeup the guest receive thread */
180 RTSEMEVENT EventRecv;
181 /** event to wakeup the guest urgent receive thread */
182 RTSEMEVENT EventUrgRecv;
183 /** Receive Req queue (deliver packets to the guest) */
184 RTREQQUEUE hRecvReqQueue;
185 /** Receive Urgent Req queue (deliver packets to the guest). */
186 RTREQQUEUE hUrgRecvReqQueue;
187
188 /** makes access to device func RecvAvail and Recv atomical. */
189 RTCRITSECT DevAccessLock;
190 /** Number of in-flight urgent packets. */
191 volatile uint32_t cUrgPkts;
192 /** Number of in-flight regular packets. */
193 volatile uint32_t cPkts;
194
195 /** Transmit lock taken by BeginXmit and released by EndXmit. */
196 RTCRITSECT XmitLock;
197} DRVNAT;
198AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
199/** Pointer to the NAT driver instance data. */
200typedef DRVNAT *PDRVNAT;
201
202
203/*******************************************************************************
204* Internal Functions *
205*******************************************************************************/
206static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho);
207
208
209static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
210{
211 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
212
213 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
214 return VINF_SUCCESS;
215
216 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
217 {
218 RTReqQueueProcess(pThis->hRecvReqQueue, 0);
219 if (ASMAtomicReadU32(&pThis->cPkts) == 0)
220 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
221 }
222 return VINF_SUCCESS;
223}
224
225
226static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
227{
228 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
229 int rc;
230 rc = RTSemEventSignal(pThis->EventRecv);
231
232 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
233 return VINF_SUCCESS;
234}
235
236static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
237{
238 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
239
240 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
241 return VINF_SUCCESS;
242
243 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
244 {
245 RTReqQueueProcess(pThis->hUrgRecvReqQueue, 0);
246 if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
247 {
248 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
249 AssertRC(rc);
250 }
251 }
252 return VINF_SUCCESS;
253}
254
255static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
256{
257 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
258 int rc = RTSemEventSignal(pThis->EventUrgRecv);
259 AssertRC(rc);
260
261 return VINF_SUCCESS;
262}
263
264static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
265{
266 int rc = RTCritSectEnter(&pThis->DevAccessLock);
267 AssertRC(rc);
268 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
269 if (RT_SUCCESS(rc))
270 {
271 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
272 AssertRC(rc);
273 }
274 else if ( rc != VERR_TIMEOUT
275 && rc != VERR_INTERRUPTED)
276 {
277 AssertRC(rc);
278 }
279
280 rc = RTCritSectLeave(&pThis->DevAccessLock);
281 AssertRC(rc);
282
283 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
284 if (ASMAtomicDecU32(&pThis->cUrgPkts) == 0)
285 {
286 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
287 drvNATNotifyNATThread(pThis, "drvNATUrgRecvWorker");
288 }
289}
290
291
292static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
293{
294 int rc;
295 STAM_PROFILE_START(&pThis->StatNATRecv, a);
296
297
298 while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
299 {
300 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
301 if ( RT_FAILURE(rc)
302 && ( rc == VERR_TIMEOUT
303 || rc == VERR_INTERRUPTED))
304 goto done_unlocked;
305 }
306
307 rc = RTCritSectEnter(&pThis->DevAccessLock);
308 AssertRC(rc);
309
310 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
311 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
312 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
313
314 if (RT_SUCCESS(rc))
315 {
316 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
317 AssertRC(rc);
318 }
319 else if ( rc != VERR_TIMEOUT
320 && rc != VERR_INTERRUPTED)
321 {
322 AssertRC(rc);
323 }
324
325 rc = RTCritSectLeave(&pThis->DevAccessLock);
326 AssertRC(rc);
327
328done_unlocked:
329 slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
330 ASMAtomicDecU32(&pThis->cPkts);
331
332 drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
333
334 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
335}
336
337/**
338 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
339 *
340 * @param pThis Pointer to the NAT instance.
341 * @param pSgBuf The S/G buffer to free.
342 */
343static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
344{
345 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
346 pSgBuf->fFlags = 0;
347 if (pSgBuf->pvAllocator)
348 {
349 Assert(!pSgBuf->pvUser);
350 slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator, NULL);
351 pSgBuf->pvAllocator = NULL;
352 }
353 else if (pSgBuf->pvUser)
354 {
355 RTMemFree(pSgBuf->aSegs[0].pvSeg);
356 pSgBuf->aSegs[0].pvSeg = NULL;
357 RTMemFree(pSgBuf->pvUser);
358 pSgBuf->pvUser = NULL;
359 }
360 RTMemFree(pSgBuf);
361}
362
363/**
364 * Worker function for drvNATSend().
365 *
366 * @param pThis Pointer to the NAT instance.
367 * @param pSgBuf The scatter/gather buffer.
368 * @thread NAT
369 */
370static void drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
371{
372 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
373 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
374 {
375 struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
376 if (m)
377 {
378 /*
379 * A normal frame.
380 */
381 pSgBuf->pvAllocator = NULL;
382 slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
383 }
384 else
385 {
386 /*
387 * GSO frame, need to segment it.
388 */
389 /** @todo Make the NAT engine grok large frames? Could be more efficient... */
390#if 0 /* this is for testing PDMNetGsoCarveSegmentQD. */
391 uint8_t abHdrScratch[256];
392#endif
393 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
394 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
395 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
396 for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
397 {
398 size_t cbSeg;
399 void *pvSeg;
400 m = slirp_ext_m_get(pThis->pNATState, pGso->cbHdrsTotal + pGso->cbMaxSeg, &pvSeg, &cbSeg);
401 if (!m)
402 break;
403
404#if 1
405 uint32_t cbPayload, cbHdrs;
406 uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
407 iSeg, cSegs, (uint8_t *)pvSeg, &cbHdrs, &cbPayload);
408 memcpy((uint8_t *)pvSeg + cbHdrs, pbFrame + offPayload, cbPayload);
409
410 slirp_input(pThis->pNATState, m, cbPayload + cbHdrs);
411#else
412 uint32_t cbSegFrame;
413 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
414 iSeg, cSegs, &cbSegFrame);
415 memcpy((uint8_t *)pvSeg, pvSegFrame, cbSegFrame);
416
417 slirp_input(pThis->pNATState, m, cbSegFrame);
418#endif
419 }
420 }
421 }
422 drvNATFreeSgBuf(pThis, pSgBuf);
423
424 /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf semantics. */
425}
426
427/**
428 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
429 */
430static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
431{
432 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
433 int rc = RTCritSectTryEnter(&pThis->XmitLock);
434 if (RT_FAILURE(rc))
435 {
436 /** @todo Kick the worker thread when we have one... */
437 rc = VERR_TRY_AGAIN;
438 }
439 return rc;
440}
441
442/**
443 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
444 */
445static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
446 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
447{
448 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
449 Assert(RTCritSectIsOwner(&pThis->XmitLock));
450
451 /*
452 * Drop the incoming frame if the NAT thread isn't running.
453 */
454 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
455 {
456 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
457 return VERR_NET_NO_NETWORK;
458 }
459
460 /*
461 * Allocate a scatter/gather buffer and an mbuf.
462 */
463 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
464 if (!pSgBuf)
465 return VERR_NO_MEMORY;
466 if (!pGso)
467 {
468 pSgBuf->pvUser = NULL;
469 pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
470 &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
471 if (!pSgBuf->pvAllocator)
472 {
473 RTMemFree(pSgBuf);
474 return VERR_TRY_AGAIN;
475 }
476 }
477 else
478 {
479 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
480 pSgBuf->pvAllocator = NULL;
481 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
482 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
483 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
484 {
485 RTMemFree(pSgBuf->aSegs[0].pvSeg);
486 RTMemFree(pSgBuf->pvUser);
487 RTMemFree(pSgBuf);
488 return VERR_TRY_AGAIN;
489 }
490 }
491
492 /*
493 * Initialize the S/G buffer and return.
494 */
495 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
496 pSgBuf->cbUsed = 0;
497 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
498 pSgBuf->cSegs = 1;
499
500#if 0 /* poison */
501 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
502#endif
503 *ppSgBuf = pSgBuf;
504 return VINF_SUCCESS;
505}
506
507/**
508 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
509 */
510static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
511{
512 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
513 Assert(RTCritSectIsOwner(&pThis->XmitLock));
514 drvNATFreeSgBuf(pThis, pSgBuf);
515 return VINF_SUCCESS;
516}
517
518/**
519 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
520 */
521static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
522{
523 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
524 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
525 Assert(RTCritSectIsOwner(&pThis->XmitLock));
526
527 int rc;
528 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
529 {
530 /* Set an FTM checkpoint as this operation changes the state permanently. */
531 PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
532
533
534 RTREQQUEUE hQueue = pThis->hSlirpReqQueue;
535
536 rc = RTReqQueueCallEx(hQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
537 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
538 if (RT_SUCCESS(rc))
539 {
540 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
541 return VINF_SUCCESS;
542 }
543
544 rc = VERR_NET_NO_BUFFER_SPACE;
545 }
546 else
547 rc = VERR_NET_DOWN;
548 drvNATFreeSgBuf(pThis, pSgBuf);
549 return rc;
550}
551
552/**
553 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
554 */
555static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
556{
557 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
558 RTCritSectLeave(&pThis->XmitLock);
559}
560
561/**
562 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
563 */
564static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
565{
566 int rc;
567#ifndef RT_OS_WINDOWS
568 /* kick poll() */
569 size_t cbIgnored;
570 rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
571#else
572 /* kick WSAWaitForMultipleEvents */
573 rc = WSASetEvent(pThis->hWakeupEvent);
574#endif
575 AssertRC(rc);
576}
577
578/**
579 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
580 */
581static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
582{
583 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
584 /* nothing to do */
585}
586
587/**
588 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
589 * @thread "NAT" thread.
590 */
591static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
592{
593 pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
594 switch (enmLinkState)
595 {
596 case PDMNETWORKLINKSTATE_UP:
597 LogRel(("NAT: link up\n"));
598 slirp_link_up(pThis->pNATState);
599 break;
600
601 case PDMNETWORKLINKSTATE_DOWN:
602 case PDMNETWORKLINKSTATE_DOWN_RESUME:
603 LogRel(("NAT: link down\n"));
604 slirp_link_down(pThis->pNATState);
605 break;
606
607 default:
608 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
609 }
610}
611
612/**
613 * Notification on link status changes.
614 *
615 * @param pInterface Pointer to the interface structure containing the called function pointer.
616 * @param enmLinkState The new link state.
617 * @thread EMT
618 */
619static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
620{
621 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
622
623 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
624
625 /* Don't queue new requests when the NAT thread is about to stop.
626 * But the VM could also be paused. So memorize the desired state. */
627 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
628 {
629 pThis->enmLinkStateWant = enmLinkState;
630 return;
631 }
632
633 PRTREQ pReq;
634 int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
635 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
636 if (RT_LIKELY(rc == VERR_TIMEOUT))
637 {
638 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
639 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
640 AssertRC(rc);
641 }
642 else
643 AssertRC(rc);
644 RTReqRelease(pReq);
645}
646
647static void drvNATNotifyApplyPortForwardCommand(PDRVNAT pThis, bool fRemove,
648 bool fUdp, const char *pHostIp,
649 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
650{
651 RTMAC Mac;
652 RT_ZERO(Mac); /* can't get MAC here */
653 if (pThis->pIAboveConfig)
654 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
655
656 struct in_addr guestIp, hostIp;
657
658 if ( pHostIp == NULL
659 || inet_aton(pHostIp, &hostIp) == 0)
660 hostIp.s_addr = INADDR_ANY;
661
662 if ( pGuestIp == NULL
663 || inet_aton(pGuestIp, &guestIp) == 0)
664 guestIp.s_addr = pThis->GuestIP;
665
666 if (fRemove)
667 slirp_remove_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
668 else
669 slirp_add_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort, Mac.au8);
670}
671
672DECLCALLBACK(int) drvNATNetworkNatConfig_RedirectRuleCommand(PPDMINETWORKNATCONFIG pInterface, bool fRemove,
673 bool fUdp, const char *pHostIp,
674 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
675{
676 LogFlowFunc(("fRemove=%d, fUdp=%d, pHostIp=%s, u16HostPort=%u, pGuestIp=%s, u16GuestPort=%u\n",
677 RT_BOOL(fRemove), RT_BOOL(fUdp), pHostIp, u16HostPort, pGuestIp,
678 u16GuestPort));
679 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
680 PRTREQ pReq;
681 int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
682 (PFNRT)drvNATNotifyApplyPortForwardCommand, 7, pThis, fRemove,
683 fUdp, pHostIp, u16HostPort, pGuestIp, u16GuestPort);
684 if (RT_LIKELY(rc == VERR_TIMEOUT))
685 {
686 drvNATNotifyNATThread(pThis, "drvNATNetworkNatConfig_RedirectRuleCommand");
687 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
688 AssertRC(rc);
689 }
690 else
691 AssertRC(rc);
692
693 RTReqRelease(pReq);
694 port_forwarding_done:
695 return rc;
696}
697
698/**
699 * NAT thread handling the slirp stuff.
700 *
701 * The slirp implementation is single-threaded so we execute this enginre in a
702 * dedicated thread. We take care that this thread does not become the
703 * bottleneck: If the guest wants to send, a request is enqueued into the
704 * hSlirpReqQueue and handled asynchronously by this thread. If this thread
705 * wants to deliver packets to the guest, it enqueues a request into
706 * hRecvReqQueue which is later handled by the Recv thread.
707 */
708static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
709{
710 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
711 int nFDs = -1;
712#ifdef RT_OS_WINDOWS
713 HANDLE *phEvents = slirp_get_events(pThis->pNATState);
714 unsigned int cBreak = 0;
715#else /* RT_OS_WINDOWS */
716 unsigned int cPollNegRet = 0;
717#endif /* !RT_OS_WINDOWS */
718
719 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
720
721 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
722 return VINF_SUCCESS;
723
724 if (pThis->enmLinkStateWant != pThis->enmLinkState)
725 drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
726
727 /*
728 * Polling loop.
729 */
730 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
731 {
732 /*
733 * To prevent concurrent execution of sending/receiving threads
734 */
735#ifndef RT_OS_WINDOWS
736 nFDs = slirp_get_nsock(pThis->pNATState);
737 /* allocation for all sockets + Management pipe */
738 struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
739 if (polls == NULL)
740 return VERR_NO_MEMORY;
741
742 /* don't pass the management pipe */
743 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
744
745 polls[0].fd = RTPipeToNative(pThis->hPipeRead);
746 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
747 polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
748 polls[0].revents = 0;
749
750 int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
751 if (cChangedFDs < 0)
752 {
753 if (errno == EINTR)
754 {
755 Log2(("NAT: signal was caught while sleep on poll\n"));
756 /* No error, just process all outstanding requests but don't wait */
757 cChangedFDs = 0;
758 }
759 else if (cPollNegRet++ > 128)
760 {
761 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
762 cPollNegRet = 0;
763 }
764 }
765
766 if (cChangedFDs >= 0)
767 {
768 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
769 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
770 {
771 /* drain the pipe
772 *
773 * Note! drvNATSend decoupled so we don't know how many times
774 * device's thread sends before we've entered multiplex,
775 * so to avoid false alarm drain pipe here to the very end
776 *
777 * @todo: Probably we should counter drvNATSend to count how
778 * deep pipe has been filed before drain.
779 *
780 */
781 /** @todo XXX: Make it reading exactly we need to drain the
782 * pipe.*/
783 char ch;
784 size_t cbRead;
785 RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
786 }
787 }
788 /* process _all_ outstanding requests but don't wait */
789 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
790 RTMemFree(polls);
791
792#else /* RT_OS_WINDOWS */
793 nFDs = -1;
794 slirp_select_fill(pThis->pNATState, &nFDs);
795 DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
796 slirp_get_timeout_ms(pThis->pNATState),
797 FALSE);
798 if ( (dwEvent < WSA_WAIT_EVENT_0 || dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
799 && dwEvent != WSA_WAIT_TIMEOUT)
800 {
801 int error = WSAGetLastError();
802 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
803 RTAssertPanic();
804 }
805
806 if (dwEvent == WSA_WAIT_TIMEOUT)
807 {
808 /* only check for slow/fast timers */
809 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
810 continue;
811 }
812 /* poll the sockets in any case */
813 Log2(("%s: poll\n", __FUNCTION__));
814 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(dwEvent == WSA_WAIT_EVENT_0));
815 /* process _all_ outstanding requests but don't wait */
816 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
817# ifdef VBOX_NAT_DELAY_HACK
818 if (cBreak++ > 128)
819 {
820 cBreak = 0;
821 RTThreadSleep(2);
822 }
823# endif
824#endif /* RT_OS_WINDOWS */
825 }
826
827 return VINF_SUCCESS;
828}
829
830
831/**
832 * Unblock the send thread so it can respond to a state change.
833 *
834 * @returns VBox status code.
835 * @param pDevIns The pcnet device instance.
836 * @param pThread The send thread.
837 */
838static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
839{
840 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
841
842 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
843 return VINF_SUCCESS;
844}
845
846/**
847 * Function called by slirp to check if it's possible to feed incoming data to the network port.
848 * @returns 1 if possible.
849 * @returns 0 if not possible.
850 */
851int slirp_can_output(void *pvUser)
852{
853 return 1;
854}
855
856void slirp_push_recv_thread(void *pvUser)
857{
858 PDRVNAT pThis = (PDRVNAT)pvUser;
859 Assert(pThis);
860 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
861}
862
863void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
864{
865 PDRVNAT pThis = (PDRVNAT)pvUser;
866 Assert(pThis);
867
868 PRTREQ pReq = NULL;
869
870 /* don't queue new requests when the NAT thread is about to stop */
871 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
872 return;
873
874 ASMAtomicIncU32(&pThis->cUrgPkts);
875 int rc = RTReqQueueCallEx(pThis->hUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
876 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
877 AssertRC(rc);
878 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
879}
880
881/**
882 * Function called by slirp to wake up device after VERR_TRY_AGAIN
883 */
884void slirp_output_pending(void *pvUser)
885{
886 PDRVNAT pThis = (PDRVNAT)pvUser;
887 Assert(pThis);
888 LogFlowFuncEnter();
889 pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
890 LogFlowFuncLeave();
891}
892
893/**
894 * Function called by slirp to feed incoming data to the NIC.
895 */
896void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
897{
898 PDRVNAT pThis = (PDRVNAT)pvUser;
899 Assert(pThis);
900
901 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
902 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
903
904 PRTREQ pReq = NULL;
905
906 /* don't queue new requests when the NAT thread is about to stop */
907 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
908 return;
909
910 ASMAtomicIncU32(&pThis->cPkts);
911 int rc = RTReqQueueCallEx(pThis->hRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
912 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
913 AssertRC(rc);
914 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
915 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
916 LogFlowFuncLeave();
917}
918
919
920/**
921 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
922 */
923static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
924{
925 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
926 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
927
928 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
929 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
930 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKNATCONFIG, &pThis->INetworkNATCfg);
931 return NULL;
932}
933
934
935/**
936 * Get the MAC address into the slirp stack.
937 *
938 * Called by drvNATLoadDone and drvNATPowerOn.
939 */
940static void drvNATSetMac(PDRVNAT pThis)
941{
942 if (pThis->pIAboveConfig)
943 {
944 RTMAC Mac;
945 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
946 /* Re-activate the port forwarding. If */
947 slirp_set_ethaddr_and_activate_port_forwarding(pThis->pNATState, Mac.au8, pThis->GuestIP);
948 }
949}
950
951
952/**
953 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
954 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
955 * (usually done during guest boot).
956 */
957static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
958{
959 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
960 drvNATSetMac(pThis);
961 return VINF_SUCCESS;
962}
963
964
965/**
966 * Some guests might not use DHCP to retrieve an IP but use a static IP.
967 */
968static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
969{
970 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
971 drvNATSetMac(pThis);
972}
973
974
975/**
976 * Info handler.
977 */
978static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
979{
980 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
981 slirp_info(pThis->pNATState, pHlp, pszArgs);
982}
983
984#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
985static int drvNATConstructDNSMappings(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pMappingsCfg)
986{
987 int rc = VINF_SUCCESS;
988 LogFlowFunc(("ENTER: iInstance:%d\n", iInstance));
989 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pMappingsCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
990 {
991 if (!CFGMR3AreValuesValid(pNode, "HostName\0HostNamePattern\0HostIP\0"))
992 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
993 N_("Unknown configuration in dns mapping"));
994 char szHostNameOrPattern[255];
995 bool fMatch = false; /* false used for equal matching, and true if wildcard pattern is used. */
996 RT_ZERO(szHostNameOrPattern);
997 GET_STRING(rc, pThis, pNode, "HostName", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
998 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
999 {
1000 GET_STRING(rc, pThis, pNode, "HostNamePattern", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
1001 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1002 {
1003 char szNodeName[225];
1004 RT_ZERO(szNodeName);
1005 CFGMR3GetName(pNode, szNodeName, sizeof(szNodeName));
1006 LogRel(("NAT: Neither 'HostName' nor 'HostNamePattern' is specified for mapping %s\n", szNodeName));
1007 continue;
1008 }
1009 fMatch = true;
1010 }
1011 struct in_addr HostIP;
1012 GETIP_DEF(rc, pThis, pNode, HostIP, INADDR_ANY);
1013 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1014 {
1015 LogRel(("NAT: DNS mapping %s is ignored (address not pointed)\n", szHostNameOrPattern));
1016 continue;
1017 }
1018 slirp_add_host_resolver_mapping(pThis->pNATState, fMatch ? NULL : szHostNameOrPattern, fMatch ? szHostNameOrPattern : NULL, HostIP.s_addr);
1019 }
1020 LogFlowFunc(("LEAVE: %Rrc\n", rc));
1021 return rc;
1022}
1023#endif /* !VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER */
1024
1025
1026/**
1027 * Sets up the redirectors.
1028 *
1029 * @returns VBox status code.
1030 * @param pCfg The configuration handle.
1031 */
1032static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network)
1033{
1034 RTMAC Mac;
1035 RT_ZERO(Mac); /* can't get MAC here */
1036
1037 /*
1038 * Enumerate redirections.
1039 */
1040 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
1041 {
1042#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1043 char szNodeName[32];
1044 CFGMR3GetName(pNode, szNodeName, 32);
1045 if ( !RTStrICmp(szNodeName, "HostResolverMappings")
1046 || !RTStrICmp(szNodeName, "AttachedDriver"))
1047 continue;
1048#endif
1049 /*
1050 * Validate the port forwarding config.
1051 */
1052 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
1053 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1054 N_("Unknown configuration in port forwarding"));
1055
1056 /* protocol type */
1057 bool fUDP;
1058 char szProtocol[32];
1059 int rc;
1060 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
1061 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1062 {
1063 fUDP = false;
1064 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
1065 }
1066 else if (RT_SUCCESS(rc))
1067 {
1068 if (!RTStrICmp(szProtocol, "TCP"))
1069 fUDP = false;
1070 else if (!RTStrICmp(szProtocol, "UDP"))
1071 fUDP = true;
1072 else
1073 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1074 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
1075 iInstance, szProtocol);
1076 }
1077 else
1078 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS,
1079 N_("NAT#%d: configuration query for \"Protocol\" failed"),
1080 iInstance);
1081 /* host port */
1082 int32_t iHostPort;
1083 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
1084
1085 /* guest port */
1086 int32_t iGuestPort;
1087 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
1088
1089 /* guest address */
1090 struct in_addr GuestIP;
1091 /* @todo (vvl) use CTL_* */
1092 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
1093
1094 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
1095 * is not documented. Without */
1096 if (pThis->GuestIP == INADDR_ANY)
1097 pThis->GuestIP = GuestIP.s_addr;
1098
1099 /*
1100 * Call slirp about it.
1101 */
1102 struct in_addr BindIP;
1103 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
1104 if (slirp_add_redirect(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
1105 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1106 N_("NAT#%d: configuration error: failed to set up "
1107 "redirection of %d to %d. Probably a conflict with "
1108 "existing services or other rules"), iInstance, iHostPort,
1109 iGuestPort);
1110 } /* for each redir rule */
1111
1112 return VINF_SUCCESS;
1113}
1114
1115
1116/**
1117 * Destruct a driver instance.
1118 *
1119 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1120 * resources can be freed correctly.
1121 *
1122 * @param pDrvIns The driver instance data.
1123 */
1124static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1125{
1126 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1127 LogFlow(("drvNATDestruct:\n"));
1128 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1129
1130 if (pThis->pNATState)
1131 {
1132 slirp_term(pThis->pNATState);
1133 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1134#ifdef VBOX_WITH_STATISTICS
1135# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1136# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1137# include "counters.h"
1138#endif
1139 pThis->pNATState = NULL;
1140 }
1141
1142 RTReqQueueDestroy(pThis->hSlirpReqQueue);
1143 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1144
1145 RTReqQueueDestroy(pThis->hUrgRecvReqQueue);
1146 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1147
1148 RTSemEventDestroy(pThis->EventRecv);
1149 pThis->EventRecv = NIL_RTSEMEVENT;
1150
1151 RTSemEventDestroy(pThis->EventUrgRecv);
1152 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1153
1154 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1155 RTCritSectDelete(&pThis->DevAccessLock);
1156
1157 if (RTCritSectIsInitialized(&pThis->XmitLock))
1158 RTCritSectDelete(&pThis->XmitLock);
1159}
1160
1161
1162/**
1163 * Construct a NAT network transport driver instance.
1164 *
1165 * @copydoc FNPDMDRVCONSTRUCT
1166 */
1167static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1168{
1169 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1170 LogFlow(("drvNATConstruct:\n"));
1171 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1172
1173 /*
1174 * Validate the config.
1175 */
1176 if (!CFGMR3AreValuesValid(pCfg,
1177 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1178 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1179 "SlirpMTU\0AliasMode\0"
1180 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"
1181 "ICMPCacheLimit\0"
1182 "SoMaxConnection\0"
1183#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1184 "HostResolverMappings\0"
1185#endif
1186 ))
1187 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1188 N_("Unknown NAT configuration option, only supports PassDomain,"
1189 " TFTPPrefix, BootFile and Network"));
1190
1191 /*
1192 * Init the static parts.
1193 */
1194 pThis->pDrvIns = pDrvIns;
1195 pThis->pNATState = NULL;
1196 pThis->pszTFTPPrefix = NULL;
1197 pThis->pszBootFile = NULL;
1198 pThis->pszNextServer = NULL;
1199 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1200 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1201 pThis->EventRecv = NIL_RTSEMEVENT;
1202 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1203
1204 /* IBase */
1205 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1206
1207 /* INetwork */
1208 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1209 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1210 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1211 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1212 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1213 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1214 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1215
1216 /* NAT engine configuration */
1217 pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfig_RedirectRuleCommand;
1218
1219 /*
1220 * Get the configuration settings.
1221 */
1222 int rc;
1223 bool fPassDomain = true;
1224 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1225
1226 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1227 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1228 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1229
1230 int fDNSProxy = 0;
1231 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1232 int fUseHostResolver = 0;
1233 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1234 int MTU = 1500;
1235 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1236 int i32AliasMode = 0;
1237 int i32MainAliasMode = 0;
1238 GET_S32(rc, pThis, pCfg, "AliasMode", i32MainAliasMode);
1239 int iIcmpCacheLimit = 100;
1240 GET_S32(rc, pThis, pCfg, "ICMPCacheLimit", iIcmpCacheLimit);
1241
1242 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1243 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1244 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1245 int i32SoMaxConn = 10;
1246 GET_S32(rc, pThis, pCfg, "SoMaxConnection", i32SoMaxConn);
1247 /*
1248 * Query the network port interface.
1249 */
1250 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1251 if (!pThis->pIAboveNet)
1252 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1253 N_("Configuration error: the above device/driver didn't "
1254 "export the network port interface"));
1255 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1256 if (!pThis->pIAboveConfig)
1257 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1258 N_("Configuration error: the above device/driver didn't "
1259 "export the network config interface"));
1260
1261 /* Generate a network address for this network card. */
1262 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1263 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1264 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1265 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1266 "missing network"),
1267 pDrvIns->iInstance, szNetwork);
1268
1269 RTIPV4ADDR Network;
1270 RTIPV4ADDR Netmask;
1271 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1272 if (RT_FAILURE(rc))
1273 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1274 "network '%s' describes not a valid IPv4 network"),
1275 pDrvIns->iInstance, szNetwork);
1276
1277 /*
1278 * Initialize slirp.
1279 */
1280 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network), Netmask,
1281 fPassDomain, !!fUseHostResolver, i32AliasMode,
1282 iIcmpCacheLimit, pThis);
1283 if (RT_SUCCESS(rc))
1284 {
1285 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1286 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1287 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1288 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1289 slirp_set_mtu(pThis->pNATState, MTU);
1290 slirp_set_somaxconn(pThis->pNATState, i32SoMaxConn);
1291 char *pszBindIP = NULL;
1292 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1293 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1294 if (rc != 0 && pszBindIP && *pszBindIP)
1295 LogRel(("NAT: value of BindIP has been ignored\n"));
1296
1297 if(pszBindIP != NULL)
1298 MMR3HeapFree(pszBindIP);
1299#define SLIRP_SET_TUNING_VALUE(name, setter) \
1300 do \
1301 { \
1302 int len = 0; \
1303 rc = CFGMR3QueryS32(pCfg, name, &len); \
1304 if (RT_SUCCESS(rc)) \
1305 setter(pThis->pNATState, len); \
1306 } while(0)
1307
1308 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1309 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1310 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1311 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1312
1313 slirp_register_statistics(pThis->pNATState, pDrvIns);
1314#ifdef VBOX_WITH_STATISTICS
1315# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1316# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1317# include "counters.h"
1318#endif
1319
1320#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1321 PCFGMNODE pMappingsCfg = CFGMR3GetChild(pCfg, "HostResolverMappings");
1322
1323 if (pMappingsCfg)
1324 {
1325 rc = drvNATConstructDNSMappings(pDrvIns->iInstance, pThis, pMappingsCfg);
1326 AssertRC(rc);
1327 }
1328#endif
1329 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1330 if (RT_SUCCESS(rc))
1331 {
1332 /*
1333 * Register a load done notification to get the MAC address into the slirp
1334 * engine after we loaded a guest state.
1335 */
1336 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1337 AssertLogRelRCReturn(rc, rc);
1338
1339 rc = RTReqQueueCreate(&pThis->hSlirpReqQueue);
1340 AssertLogRelRCReturn(rc, rc);
1341
1342 rc = RTReqQueueCreate(&pThis->hRecvReqQueue);
1343 AssertLogRelRCReturn(rc, rc);
1344
1345 rc = RTReqQueueCreate(&pThis->hUrgRecvReqQueue);
1346 AssertLogRelRCReturn(rc, rc);
1347
1348 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1349 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1350 AssertRCReturn(rc, rc);
1351
1352 rc = RTSemEventCreate(&pThis->EventRecv);
1353 AssertRCReturn(rc, rc);
1354
1355 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1356 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1357 AssertRCReturn(rc, rc);
1358
1359 rc = RTSemEventCreate(&pThis->EventRecv);
1360 AssertRCReturn(rc, rc);
1361
1362 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1363 AssertRCReturn(rc, rc);
1364
1365 rc = RTCritSectInit(&pThis->DevAccessLock);
1366 AssertRCReturn(rc, rc);
1367
1368 rc = RTCritSectInit(&pThis->XmitLock);
1369 AssertRCReturn(rc, rc);
1370
1371 char szTmp[128];
1372 RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
1373 PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
1374
1375#ifndef RT_OS_WINDOWS
1376 /*
1377 * Create the control pipe.
1378 */
1379 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
1380 AssertRCReturn(rc, rc);
1381#else
1382 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1383 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1384 VBOX_WAKEUP_EVENT_INDEX);
1385#endif
1386
1387 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1388 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1389 AssertRCReturn(rc, rc);
1390
1391 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1392
1393 /* might return VINF_NAT_DNS */
1394 return rc;
1395 }
1396
1397 /* failure path */
1398 slirp_term(pThis->pNATState);
1399 pThis->pNATState = NULL;
1400 }
1401 else
1402 {
1403 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1404 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1405 }
1406
1407 return rc;
1408}
1409
1410
1411/**
1412 * NAT network transport driver registration record.
1413 */
1414const PDMDRVREG g_DrvNAT =
1415{
1416 /* u32Version */
1417 PDM_DRVREG_VERSION,
1418 /* szName */
1419 "NAT",
1420 /* szRCMod */
1421 "",
1422 /* szR0Mod */
1423 "",
1424 /* pszDescription */
1425 "NAT Network Transport Driver",
1426 /* fFlags */
1427 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1428 /* fClass. */
1429 PDM_DRVREG_CLASS_NETWORK,
1430 /* cMaxInstances */
1431 ~0U,
1432 /* cbInstance */
1433 sizeof(DRVNAT),
1434 /* pfnConstruct */
1435 drvNATConstruct,
1436 /* pfnDestruct */
1437 drvNATDestruct,
1438 /* pfnRelocate */
1439 NULL,
1440 /* pfnIOCtl */
1441 NULL,
1442 /* pfnPowerOn */
1443 drvNATPowerOn,
1444 /* pfnReset */
1445 NULL,
1446 /* pfnSuspend */
1447 NULL,
1448 /* pfnResume */
1449 NULL,
1450 /* pfnAttach */
1451 NULL,
1452 /* pfnDetach */
1453 NULL,
1454 /* pfnPowerOff */
1455 NULL,
1456 /* pfnSoftReset */
1457 NULL,
1458 /* u32EndVersion */
1459 PDM_DRVREG_VERSION
1460};
1461
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