VirtualBox

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

Last change on this file since 45761 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • 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 45061 2013-03-18 14:09:03Z 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, PRTNETADDRIPV4 pNetwork)
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 GETIP_DEF(rc, pThis, pNode, GuestIP, RT_H2N_U32(pNetwork->u | CTL_GUEST));
1092
1093 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
1094 * is not documented. Without */
1095 if (pThis->GuestIP == INADDR_ANY)
1096 pThis->GuestIP = GuestIP.s_addr;
1097
1098 /*
1099 * Call slirp about it.
1100 */
1101 struct in_addr BindIP;
1102 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
1103 if (slirp_add_redirect(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
1104 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
1105 N_("NAT#%d: configuration error: failed to set up "
1106 "redirection of %d to %d. Probably a conflict with "
1107 "existing services or other rules"), iInstance, iHostPort,
1108 iGuestPort);
1109 } /* for each redir rule */
1110
1111 return VINF_SUCCESS;
1112}
1113
1114
1115/**
1116 * Destruct a driver instance.
1117 *
1118 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1119 * resources can be freed correctly.
1120 *
1121 * @param pDrvIns The driver instance data.
1122 */
1123static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1124{
1125 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1126 LogFlow(("drvNATDestruct:\n"));
1127 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1128
1129 if (pThis->pNATState)
1130 {
1131 slirp_term(pThis->pNATState);
1132 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1133#ifdef VBOX_WITH_STATISTICS
1134# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1135# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1136# include "counters.h"
1137#endif
1138 pThis->pNATState = NULL;
1139 }
1140
1141 RTReqQueueDestroy(pThis->hSlirpReqQueue);
1142 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1143
1144 RTReqQueueDestroy(pThis->hUrgRecvReqQueue);
1145 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1146
1147 RTSemEventDestroy(pThis->EventRecv);
1148 pThis->EventRecv = NIL_RTSEMEVENT;
1149
1150 RTSemEventDestroy(pThis->EventUrgRecv);
1151 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1152
1153 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1154 RTCritSectDelete(&pThis->DevAccessLock);
1155
1156 if (RTCritSectIsInitialized(&pThis->XmitLock))
1157 RTCritSectDelete(&pThis->XmitLock);
1158}
1159
1160
1161/**
1162 * Construct a NAT network transport driver instance.
1163 *
1164 * @copydoc FNPDMDRVCONSTRUCT
1165 */
1166static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1167{
1168 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1169 LogFlow(("drvNATConstruct:\n"));
1170 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1171
1172 /*
1173 * Init the static parts.
1174 */
1175 pThis->pDrvIns = pDrvIns;
1176 pThis->pNATState = NULL;
1177 pThis->pszTFTPPrefix = NULL;
1178 pThis->pszBootFile = NULL;
1179 pThis->pszNextServer = NULL;
1180 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1181 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1182 pThis->EventRecv = NIL_RTSEMEVENT;
1183 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1184
1185 /* IBase */
1186 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1187
1188 /* INetwork */
1189 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1190 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1191 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1192 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1193 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1194 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1195 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1196
1197 /* NAT engine configuration */
1198 pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfig_RedirectRuleCommand;
1199
1200 /*
1201 * Validate the config.
1202 */
1203 if (!CFGMR3AreValuesValid(pCfg,
1204 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1205 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1206 "SlirpMTU\0AliasMode\0"
1207 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"
1208 "ICMPCacheLimit\0"
1209 "SoMaxConnection\0"
1210#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1211 "HostResolverMappings\0"
1212#endif
1213 ))
1214 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1215 N_("Unknown NAT configuration option, only supports PassDomain,"
1216 " TFTPPrefix, BootFile and Network"));
1217
1218 /*
1219 * Get the configuration settings.
1220 */
1221 int rc;
1222 bool fPassDomain = true;
1223 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1224
1225 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1226 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1227 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1228
1229 int fDNSProxy = 0;
1230 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1231 int fUseHostResolver = 0;
1232 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1233 int MTU = 1500;
1234 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1235 int i32AliasMode = 0;
1236 int i32MainAliasMode = 0;
1237 GET_S32(rc, pThis, pCfg, "AliasMode", i32MainAliasMode);
1238 int iIcmpCacheLimit = 100;
1239 GET_S32(rc, pThis, pCfg, "ICMPCacheLimit", iIcmpCacheLimit);
1240
1241 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1242 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1243 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1244 int i32SoMaxConn = 10;
1245 GET_S32(rc, pThis, pCfg, "SoMaxConnection", i32SoMaxConn);
1246 /*
1247 * Query the network port interface.
1248 */
1249 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1250 if (!pThis->pIAboveNet)
1251 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1252 N_("Configuration error: the above device/driver didn't "
1253 "export the network port interface"));
1254 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1255 if (!pThis->pIAboveConfig)
1256 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1257 N_("Configuration error: the above device/driver didn't "
1258 "export the network config interface"));
1259
1260 /* Generate a network address for this network card. */
1261 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1262 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1263 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1264 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: "
1265 "missing network"),
1266 pDrvIns->iInstance, szNetwork);
1267
1268 RTNETADDRIPV4 Network, Netmask;
1269
1270 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1271 if (RT_FAILURE(rc))
1272 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1273 "network '%s' describes not a valid IPv4 network"),
1274 pDrvIns->iInstance, szNetwork);
1275
1276 /*
1277 * Initialize slirp.
1278 */
1279 rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network.u), Netmask.u,
1280 fPassDomain, !!fUseHostResolver, i32AliasMode,
1281 iIcmpCacheLimit, pThis);
1282 if (RT_SUCCESS(rc))
1283 {
1284 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1285 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1286 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1287 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1288 slirp_set_mtu(pThis->pNATState, MTU);
1289 slirp_set_somaxconn(pThis->pNATState, i32SoMaxConn);
1290 char *pszBindIP = NULL;
1291 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1292 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1293 if (rc != 0 && pszBindIP && *pszBindIP)
1294 LogRel(("NAT: value of BindIP has been ignored\n"));
1295
1296 if(pszBindIP != NULL)
1297 MMR3HeapFree(pszBindIP);
1298#define SLIRP_SET_TUNING_VALUE(name, setter) \
1299 do \
1300 { \
1301 int len = 0; \
1302 rc = CFGMR3QueryS32(pCfg, name, &len); \
1303 if (RT_SUCCESS(rc)) \
1304 setter(pThis->pNATState, len); \
1305 } while(0)
1306
1307 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1308 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1309 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1310 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1311
1312 slirp_register_statistics(pThis->pNATState, pDrvIns);
1313#ifdef VBOX_WITH_STATISTICS
1314# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1315# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1316# include "counters.h"
1317#endif
1318
1319#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
1320 PCFGMNODE pMappingsCfg = CFGMR3GetChild(pCfg, "HostResolverMappings");
1321
1322 if (pMappingsCfg)
1323 {
1324 rc = drvNATConstructDNSMappings(pDrvIns->iInstance, pThis, pMappingsCfg);
1325 AssertRC(rc);
1326 }
1327#endif
1328 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, &Network);
1329 if (RT_SUCCESS(rc))
1330 {
1331 /*
1332 * Register a load done notification to get the MAC address into the slirp
1333 * engine after we loaded a guest state.
1334 */
1335 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1336 AssertLogRelRCReturn(rc, rc);
1337
1338 rc = RTReqQueueCreate(&pThis->hSlirpReqQueue);
1339 AssertLogRelRCReturn(rc, rc);
1340
1341 rc = RTReqQueueCreate(&pThis->hRecvReqQueue);
1342 AssertLogRelRCReturn(rc, rc);
1343
1344 rc = RTReqQueueCreate(&pThis->hUrgRecvReqQueue);
1345 AssertLogRelRCReturn(rc, rc);
1346
1347 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1348 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1349 AssertRCReturn(rc, rc);
1350
1351 rc = RTSemEventCreate(&pThis->EventRecv);
1352 AssertRCReturn(rc, rc);
1353
1354 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1355 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1356 AssertRCReturn(rc, rc);
1357
1358 rc = RTSemEventCreate(&pThis->EventRecv);
1359 AssertRCReturn(rc, rc);
1360
1361 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1362 AssertRCReturn(rc, rc);
1363
1364 rc = RTCritSectInit(&pThis->DevAccessLock);
1365 AssertRCReturn(rc, rc);
1366
1367 rc = RTCritSectInit(&pThis->XmitLock);
1368 AssertRCReturn(rc, rc);
1369
1370 char szTmp[128];
1371 RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
1372 PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
1373
1374#ifndef RT_OS_WINDOWS
1375 /*
1376 * Create the control pipe.
1377 */
1378 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
1379 AssertRCReturn(rc, rc);
1380#else
1381 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1382 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1383 VBOX_WAKEUP_EVENT_INDEX);
1384#endif
1385
1386 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1387 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1388 AssertRCReturn(rc, rc);
1389
1390 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1391
1392 /* might return VINF_NAT_DNS */
1393 return rc;
1394 }
1395
1396 /* failure path */
1397 slirp_term(pThis->pNATState);
1398 pThis->pNATState = NULL;
1399 }
1400 else
1401 {
1402 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1403 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1404 }
1405
1406 return rc;
1407}
1408
1409
1410/**
1411 * NAT network transport driver registration record.
1412 */
1413const PDMDRVREG g_DrvNAT =
1414{
1415 /* u32Version */
1416 PDM_DRVREG_VERSION,
1417 /* szName */
1418 "NAT",
1419 /* szRCMod */
1420 "",
1421 /* szR0Mod */
1422 "",
1423 /* pszDescription */
1424 "NAT Network Transport Driver",
1425 /* fFlags */
1426 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1427 /* fClass. */
1428 PDM_DRVREG_CLASS_NETWORK,
1429 /* cMaxInstances */
1430 ~0U,
1431 /* cbInstance */
1432 sizeof(DRVNAT),
1433 /* pfnConstruct */
1434 drvNATConstruct,
1435 /* pfnDestruct */
1436 drvNATDestruct,
1437 /* pfnRelocate */
1438 NULL,
1439 /* pfnIOCtl */
1440 NULL,
1441 /* pfnPowerOn */
1442 drvNATPowerOn,
1443 /* pfnReset */
1444 NULL,
1445 /* pfnSuspend */
1446 NULL,
1447 /* pfnResume */
1448 NULL,
1449 /* pfnAttach */
1450 NULL,
1451 /* pfnDetach */
1452 NULL,
1453 /* pfnPowerOff */
1454 NULL,
1455 /* pfnSoftReset */
1456 NULL,
1457 /* u32EndVersion */
1458 PDM_DRVREG_VERSION
1459};
1460
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