VirtualBox

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

Last change on this file since 47589 was 47506, checked in by vboxsync, 11 years ago

NAT: Drop oversized frames coming from guest (#6821)

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