VirtualBox

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

Last change on this file since 27827 was 27827, checked in by vboxsync, 15 years ago

NAT: enabled VBOX_NAT_DELAY_HACK for trunk as well (Windows host only)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.0 KB
Line 
1/* $Id: DrvNAT.cpp 27827 2010-03-30 13:54:01Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_NAT
27#define __STDC_LIMIT_MACROS
28#define __STDC_CONSTANT_MACROS
29#include "slirp/libslirp.h"
30#include "slirp/ctl.h"
31#include <VBox/pdmdrv.h>
32#include <VBox/pdmnetifs.h>
33#include <iprt/assert.h>
34#include <iprt/file.h>
35#include <iprt/mem.h>
36#include <iprt/string.h>
37#include <iprt/critsect.h>
38#include <iprt/cidr.h>
39#include <iprt/stream.h>
40#include <iprt/uuid.h>
41
42#include "Builtins.h"
43
44#ifndef RT_OS_WINDOWS
45# include <unistd.h>
46# include <fcntl.h>
47# include <poll.h>
48# include <errno.h>
49#endif
50#ifdef RT_OS_FREEBSD
51# include <netinet/in.h>
52#endif
53#include <iprt/semaphore.h>
54#include <iprt/req.h>
55
56#define COUNTERS_INIT
57#include "counters.h"
58
59
60/*******************************************************************************
61* Defined Constants And Macros *
62*******************************************************************************/
63
64/**
65 * @todo: This is a bad hack to prevent freezing the guest during high network
66 * activity. Windows host only. This needs to be fixed properly.
67 */
68#define VBOX_NAT_DELAY_HACK
69
70#define GET_EXTRADATA(pthis, node, name, rc, type, type_name, var) \
71do { \
72 (rc) = CFGMR3Query ## type((node), name, &(var)); \
73 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
74 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
75 (pthis)->pDrvIns->iInstance); \
76} while (0)
77
78#define GET_ED_STRICT(pthis, node, name, rc, type, type_name, var) \
79do { \
80 (rc) = CFGMR3Query ## type((node), name, &(var)); \
81 if (RT_FAILURE((rc))) \
82 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
83 (pthis)->pDrvIns->iInstance); \
84} while (0)
85
86#define GET_EXTRADATA_N(pthis, node, name, rc, type, type_name, var, var_size) \
87do { \
88 (rc) = CFGMR3Query ## type((node), name, &(var), var_size); \
89 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
90 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
91 (pthis)->pDrvIns->iInstance); \
92} while (0)
93
94#define GET_BOOL(rc, pthis, node, name, var) \
95 GET_EXTRADATA(pthis, node, name, (rc), Bool, bolean, (var))
96#define GET_STRING(rc, pthis, node, name, var, var_size) \
97 GET_EXTRADATA_N(pthis, node, name, (rc), String, string, (var), (var_size))
98#define GET_STRING_ALLOC(rc, pthis, node, name, var) \
99 GET_EXTRADATA(pthis, node, name, (rc), StringAlloc, string, (var))
100#define GET_S32(rc, pthis, node, name, var) \
101 GET_EXTRADATA(pthis, node, name, (rc), S32, int, (var))
102#define GET_S32_STRICT(rc, pthis, node, name, var) \
103 GET_ED_STRICT(pthis, node, name, (rc), S32, int, (var))
104
105
106
107#define DO_GET_IP(rc, node, instance, status, x) \
108do { \
109 char sz##x[32]; \
110 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
111 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
112 (status) = inet_aton(sz ## x, &x); \
113} while (0)
114
115#define GETIP_DEF(rc, node, instance, x, def) \
116do \
117{ \
118 int status = 0; \
119 DO_GET_IP((rc), (node), (instance), status, x); \
120 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
121 x.s_addr = def; \
122} while (0)
123
124/*******************************************************************************
125* Structures and Typedefs *
126*******************************************************************************/
127/**
128 * NAT network transport driver instance data.
129 *
130 * @implements PDMINETWORKUP
131 */
132typedef struct DRVNAT
133{
134 /** The network interface. */
135 PDMINETWORKUP INetworkUp;
136 /** The port we're attached to. */
137 PPDMINETWORKDOWN pIAboveNet;
138 /** The network config of the port we're attached to. */
139 PPDMINETWORKCONFIG pIAboveConfig;
140 /** Pointer to the driver instance. */
141 PPDMDRVINS pDrvIns;
142 /** Link state */
143 PDMNETWORKLINKSTATE enmLinkState;
144 /** NAT state for this instance. */
145 PNATState pNATState;
146 /** TFTP directory prefix. */
147 char *pszTFTPPrefix;
148 /** Boot file name to provide in the DHCP server response. */
149 char *pszBootFile;
150 /** tftp server name to provide in the DHCP server response. */
151 char *pszNextServer;
152 /* polling thread */
153 PPDMTHREAD pSlirpThread;
154 /** Queue for NAT-thread-external events. */
155 PRTREQQUEUE pSlirpReqQueue;
156 /** The guest IP for port-forwarding. */
157 uint32_t GuestIP;
158 uint32_t alignment1;
159
160#ifdef VBOX_WITH_SLIRP_MT
161 PPDMTHREAD pGuestThread;
162#endif
163#ifndef RT_OS_WINDOWS
164 /** The write end of the control pipe. */
165 RTFILE PipeWrite;
166 /** The read end of the control pipe. */
167 RTFILE PipeRead;
168# if HC_ARCH_BITS == 32
169 /** Alignment padding. */
170 //uint32_t alignment2;
171# endif
172#else
173 /** for external notification */
174 HANDLE hWakeupEvent;
175#endif
176
177#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
178#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
179#include "counters.h"
180 /** thread delivering packets for receiving by the guest */
181 PPDMTHREAD pRecvThread;
182 /** thread delivering urg packets for receiving by the guest */
183 PPDMTHREAD pUrgRecvThread;
184 /** event to wakeup the guest receive thread */
185 RTSEMEVENT EventRecv;
186 /** event to wakeup the guest urgent receive thread */
187 RTSEMEVENT EventUrgRecv;
188 /** Receive Req queue (deliver packets to the guest) */
189 PRTREQQUEUE pRecvReqQueue;
190 /** Receive Urgent Req queue (deliver packets to the guest) */
191 PRTREQQUEUE pUrgRecvReqQueue;
192
193 /* makes access to device func RecvAvail and Recv atomical */
194 RTCRITSECT csDevAccess;
195 volatile uint32_t cUrgPkt;
196 volatile uint32_t cPkt;
197 PTMTIMERR3 pTmrSlow;
198 PTMTIMERR3 pTmrFast;
199} DRVNAT;
200AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
201/** Pointer the NAT driver instance data. */
202typedef DRVNAT *PDRVNAT;
203
204/**
205 * NAT queue item.
206 */
207typedef struct DRVNATQUEUITEM
208{
209 /** The core part owned by the queue manager. */
210 PDMQUEUEITEMCORE Core;
211 /** The buffer for output to guest. */
212 const uint8_t *pu8Buf;
213 /* size of buffer */
214 size_t cb;
215 void *mbuf;
216} DRVNATQUEUITEM;
217/** Pointer to a NAT queue item. */
218typedef DRVNATQUEUITEM *PDRVNATQUEUITEM;
219
220
221static void drvNATNotifyNATThread(PDRVNAT pThis);
222static DECLCALLBACK(void) drvNATSlowTimer(PPDMDRVINS pDrvIns, PTMTIMER pTimer, void *pvUser);
223static DECLCALLBACK(void) drvNATFast(PPDMDRVINS pDrvIns, PTMTIMER pTimer, void *pvUser);
224
225
226
227static DECLCALLBACK(void) drvNATSlowTimer(PPDMDRVINS pDrvIns, PTMTIMER pTimer, void *pvUser)
228{
229 Assert(pvUser);
230 PDRVNAT pThis = (PDRVNAT)pvUser;
231 drvNATNotifyNATThread(pThis);
232}
233
234static DECLCALLBACK(void) drvNATFastTimer(PPDMDRVINS pDrvIns, PTMTIMER pTimer, void *pvUser)
235{
236 Assert(pvUser);
237 PDRVNAT pThis = (PDRVNAT)pvUser;
238 drvNATNotifyNATThread(pThis);
239}
240
241
242static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
243{
244 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
245
246 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
247 return VINF_SUCCESS;
248
249 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
250 {
251 RTReqProcess(pThis->pRecvReqQueue, 0);
252 if (ASMAtomicReadU32(&pThis->cPkt) == 0)
253 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
254 }
255 return VINF_SUCCESS;
256}
257
258
259static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
260{
261 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
262 int rc;
263 rc = RTSemEventSignal(pThis->EventRecv);
264
265 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
266 return VINF_SUCCESS;
267}
268
269static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
270{
271 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
272
273 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
274 return VINF_SUCCESS;
275
276 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
277 {
278 RTReqProcess(pThis->pUrgRecvReqQueue, 0);
279 if (ASMAtomicReadU32(&pThis->cUrgPkt) == 0)
280 {
281 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
282 AssertRC(rc);
283 }
284 }
285 return VINF_SUCCESS;
286}
287
288static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
289{
290 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
291 int rc = RTSemEventSignal(pThis->EventUrgRecv);
292 AssertRC(rc);
293
294 return VINF_SUCCESS;
295}
296
297static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
298{
299 int rc = RTCritSectEnter(&pThis->csDevAccess);
300 AssertRC(rc);
301 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
302 if (RT_SUCCESS(rc))
303 {
304 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
305 AssertRC(rc);
306 }
307 else if ( RT_FAILURE(rc)
308 && ( rc == VERR_TIMEOUT
309 && rc == VERR_INTERRUPTED))
310 {
311 AssertRC(rc);
312 }
313
314 rc = RTCritSectLeave(&pThis->csDevAccess);
315 AssertRC(rc);
316
317 slirp_ext_m_free(pThis->pNATState, m);
318#ifdef VBOX_WITH_SLIRP_BSD_MBUF
319 RTMemFree(pu8Buf);
320#endif
321 if (ASMAtomicDecU32(&pThis->cUrgPkt) == 0)
322 {
323 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
324 drvNATNotifyNATThread(pThis);
325 }
326}
327
328
329static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
330{
331 int rc;
332 STAM_PROFILE_START(&pThis->StatNATRecv, a);
333
334 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
335
336 while(ASMAtomicReadU32(&pThis->cUrgPkt) != 0)
337 {
338 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
339 if ( RT_FAILURE(rc)
340 && ( rc == VERR_TIMEOUT
341 || rc == VERR_INTERRUPTED))
342 goto done_unlocked;
343 }
344
345 rc = RTCritSectEnter(&pThis->csDevAccess);
346 AssertRC(rc);
347
348 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
349 if (RT_SUCCESS(rc))
350 {
351 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
352 AssertRC(rc);
353 }
354 else if ( RT_FAILURE(rc)
355 && ( rc != VERR_TIMEOUT
356 && rc != VERR_INTERRUPTED))
357 {
358 AssertRC(rc);
359 }
360
361 rc = RTCritSectLeave(&pThis->csDevAccess);
362 AssertRC(rc);
363
364done_unlocked:
365 slirp_ext_m_free(pThis->pNATState, m);
366#ifdef VBOX_WITH_SLIRP_BSD_MBUF
367 RTMemFree(pu8Buf);
368#endif
369 ASMAtomicDecU32(&pThis->cPkt);
370
371 drvNATNotifyNATThread(pThis);
372
373 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
374 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
375}
376
377/**
378 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
379 *
380 * @param pThis Pointer to the NAT instance.
381 * @param pSgBuf The S/G buffer to free.
382 */
383static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
384{
385 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
386 pSgBuf->fFlags = 0;
387 if (pSgBuf->pvAllocator)
388 {
389 slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator);
390 pSgBuf->pvAllocator = NULL;
391 }
392 RTMemFree(pSgBuf);
393}
394
395/**
396 * Worker function for drvNATSend().
397 *
398 * @param pThis Pointer to the NAT instance.
399 * @param pSgBuf The scatter/gather buffer.
400 * @thread NAT
401 */
402static void drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
403{
404 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
405 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
406 {
407 struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
408 pSgBuf->pvAllocator = NULL;
409 slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
410 }
411 drvNATFreeSgBuf(pThis, pSgBuf);
412
413 /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf sematics. */
414}
415
416/**
417 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
418 */
419static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin, PPPDMSCATTERGATHER ppSgBuf)
420{
421 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
422
423 /*
424 * Drop the incoming frame if the NAT thread isn't running.
425 */
426 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
427 {
428 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
429 return VERR_NET_NO_NETWORK;
430 }
431
432 /*
433 * Allocate a scatter/gather buffer and an mbuf.
434 */
435 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
436 if (!pSgBuf)
437 return VERR_NO_MEMORY;
438 pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
439 &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
440 if (!pSgBuf->pvAllocator)
441 {
442 RTMemFree(pSgBuf);
443 /** @todo Implement the VERR_TRY_AGAIN sematics. */
444 return VERR_NO_MEMORY;
445 }
446
447 /*
448 * Initialize the S/G buffer and return.
449 */
450 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
451 pSgBuf->cbUsed = 0;
452 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
453 pSgBuf->pvUser = NULL;
454 pSgBuf->cSegs = 1;
455
456 *ppSgBuf = pSgBuf;
457 return VINF_SUCCESS;
458}
459
460/**
461 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
462 */
463static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
464{
465 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
466 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
467
468 int rc;
469 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
470 {
471#ifdef VBOX_WITH_SLIRP_MT
472 PRTREQQUEUE pQueue = (PRTREQQUEUE)slirp_get_queue(pThis->pNATState);
473#else
474 PRTREQQUEUE pQueue = pThis->pSlirpReqQueue;
475#endif
476 rc = RTReqCallEx(pQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
477 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
478 if (RT_SUCCESS(rc))
479 {
480 drvNATNotifyNATThread(pThis);
481 return VINF_SUCCESS;
482 }
483
484 rc = VERR_NET_NO_BUFFER_SPACE;
485 }
486 else
487 rc = VERR_NET_DOWN;
488 drvNATFreeSgBuf(pThis, pSgBuf);
489 return rc;
490}
491
492/**
493 * @interface_method_impl{PDMINETWORKUP,pfnSendDeprecated}
494 */
495static DECLCALLBACK(int) drvNATNetworkUp_SendDeprecated(PPDMINETWORKUP pInterface, const void *pvBuf, size_t cb)
496{
497 PPDMSCATTERGATHER pSgBuf;
498 int rc = drvNATNetworkUp_AllocBuf(pInterface, cb, &pSgBuf);
499 if (RT_SUCCESS(rc))
500 {
501 memcpy(pSgBuf->aSegs[0].pvSeg, pvBuf, cb);
502 pSgBuf->cbUsed = cb;
503 rc = drvNATNetworkUp_SendBuf(pInterface, pSgBuf, false);
504 }
505 LogFlow(("drvNATNetworkUp_SendDeprecated: (rc=%Rrc)\n", rc));
506 return VINF_SUCCESS;
507}
508
509/**
510 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
511 */
512static void drvNATNotifyNATThread(PDRVNAT pThis)
513{
514 int rc;
515#ifndef RT_OS_WINDOWS
516 /* kick select() */
517 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
518#else
519 /* kick WSAWaitForMultipleEvents */
520 rc = WSASetEvent(pThis->hWakeupEvent);
521#endif
522 AssertRC(rc);
523}
524
525/**
526 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
527 */
528static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
529{
530 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
531 /* nothing to do */
532}
533
534/**
535 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
536 * @thread "NAT" thread.
537 */
538static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
539{
540 pThis->enmLinkState = enmLinkState;
541
542 switch (enmLinkState)
543 {
544 case PDMNETWORKLINKSTATE_UP:
545 LogRel(("NAT: link up\n"));
546 slirp_link_up(pThis->pNATState);
547 break;
548
549 case PDMNETWORKLINKSTATE_DOWN:
550 case PDMNETWORKLINKSTATE_DOWN_RESUME:
551 LogRel(("NAT: link down\n"));
552 slirp_link_down(pThis->pNATState);
553 break;
554
555 default:
556 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
557 }
558}
559
560/**
561 * Notification on link status changes.
562 *
563 * @param pInterface Pointer to the interface structure containing the called function pointer.
564 * @param enmLinkState The new link state.
565 * @thread EMT
566 */
567static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
568{
569 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
570
571 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
572
573 /* don't queue new requests when the NAT thread is about to stop */
574 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
575 return;
576
577 PRTREQ pReq;
578 int rc = RTReqCallEx(pThis->pSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
579 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
580 if (RT_LIKELY(rc == VERR_TIMEOUT))
581 {
582 drvNATNotifyNATThread(pThis);
583 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
584 AssertRC(rc);
585 }
586 else
587 AssertRC(rc);
588 RTReqFree(pReq);
589}
590
591/**
592 * NAT thread handling the slirp stuff.
593 *
594 * The slirp implementation is single-threaded so we execute this enginre in a
595 * dedicated thread. We take care that this thread does not become the
596 * bottleneck: If the guest wants to send, a request is enqueued into the
597 * pSlirpReqQueue and handled asynchronously by this thread. If this thread
598 * wants to deliver packets to the guest, it enqueues a request into
599 * pRecvReqQueue which is later handled by the Recv thread.
600 */
601static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
602{
603 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
604 int nFDs = -1;
605 int ms;
606#ifdef RT_OS_WINDOWS
607 DWORD event;
608 HANDLE *phEvents;
609 unsigned int cBreak = 0;
610#else /* RT_OS_WINDOWS */
611 struct pollfd *polls = NULL;
612 unsigned int cPollNegRet = 0;
613#endif /* !RT_OS_WINDOWS */
614
615 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
616
617 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
618 return VINF_SUCCESS;
619
620#ifdef RT_OS_WINDOWS
621 phEvents = slirp_get_events(pThis->pNATState);
622#endif /* RT_OS_WINDOWS */
623
624 /*
625 * Polling loop.
626 */
627 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
628 {
629 nFDs = -1;
630 /*
631 * To prevent concurent execution of sending/receving threads
632 */
633#ifndef RT_OS_WINDOWS
634 nFDs = slirp_get_nsock(pThis->pNATState);
635 polls = NULL;
636 /* allocation for all sockets + Management pipe */
637 polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
638 if (polls == NULL)
639 return VERR_NO_MEMORY;
640
641 /* don't pass the managemant pipe */
642 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
643#if 0
644 ms = slirp_get_timeout_ms(pThis->pNATState);
645#else
646 ms = 0;
647#endif
648
649 polls[0].fd = pThis->PipeRead;
650 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
651 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
652 polls[0].revents = 0;
653
654 int cChangedFDs = poll(polls, nFDs + 1, ms ? ms : -1);
655 if (cChangedFDs < 0)
656 {
657 if (errno == EINTR)
658 {
659 Log2(("NAT: signal was caught while sleep on poll\n"));
660 /* No error, just process all outstanding requests but don't wait */
661 cChangedFDs = 0;
662 }
663 else if (cPollNegRet++ > 128)
664 {
665 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
666 cPollNegRet = 0;
667 }
668 }
669
670 if (cChangedFDs >= 0)
671 {
672 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
673 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
674 {
675 /* drain the pipe */
676 char ch[1];
677 size_t cbRead;
678 int counter = 0;
679 /*
680 * drvNATSend decoupled so we don't know how many times
681 * device's thread sends before we've entered multiplex,
682 * so to avoid false alarm drain pipe here to the very end
683 *
684 * @todo: Probably we should counter drvNATSend to count how
685 * deep pipe has been filed before drain.
686 *
687 * XXX:Make it reading exactly we need to drain the pipe.
688 */
689 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
690 }
691 }
692 /* process _all_ outstanding requests but don't wait */
693 RTReqProcess(pThis->pSlirpReqQueue, 0);
694 RTMemFree(polls);
695#else /* RT_OS_WINDOWS */
696 slirp_select_fill(pThis->pNATState, &nFDs);
697#if 0
698 ms = slirp_get_timeout_ms(pThis->pNATState);
699#else
700 ms = 0;
701#endif
702 struct timeval tv = { 0, ms*1000 };
703 event = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE, ms ? ms : WSA_INFINITE, FALSE);
704 if ( (event < WSA_WAIT_EVENT_0 || event > WSA_WAIT_EVENT_0 + nFDs - 1)
705 && event != WSA_WAIT_TIMEOUT)
706 {
707 int error = WSAGetLastError();
708 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", event, error));
709 RTAssertPanic();
710 }
711
712 if (event == WSA_WAIT_TIMEOUT)
713 {
714 /* only check for slow/fast timers */
715 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
716 continue;
717 }
718 /* poll the sockets in any case */
719 Log2(("%s: poll\n", __FUNCTION__));
720 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(event == WSA_WAIT_EVENT_0));
721 /* process _all_ outstanding requests but don't wait */
722 RTReqProcess(pThis->pSlirpReqQueue, 0);
723# ifdef VBOX_NAT_DELAY_HACK
724 if (cBreak++ > 128)
725 {
726 cBreak = 0;
727 RTThreadSleep(2);
728 }
729# endif
730#endif /* RT_OS_WINDOWS */
731 }
732
733 return VINF_SUCCESS;
734}
735
736
737/**
738 * Unblock the send thread so it can respond to a state change.
739 *
740 * @returns VBox status code.
741 * @param pDevIns The pcnet device instance.
742 * @param pThread The send thread.
743 */
744static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
745{
746 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
747
748 drvNATNotifyNATThread(pThis);
749 return VINF_SUCCESS;
750}
751
752#ifdef VBOX_WITH_SLIRP_MT
753
754static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
755{
756 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
757
758 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
759 return VINF_SUCCESS;
760
761 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
762 slirp_process_queue(pThis->pNATState);
763
764 return VINF_SUCCESS;
765}
766
767
768static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
769{
770 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
771
772 return VINF_SUCCESS;
773}
774
775#endif /* VBOX_WITH_SLIRP_MT */
776
777void slirp_arm_fast_timer(void *pvUser)
778{
779 PDRVNAT pThis = (PDRVNAT)pvUser;
780 Assert(pThis);
781 TMTimerSetMillies(pThis->pTmrFast, 2);
782}
783
784void slirp_arm_slow_timer(void *pvUser)
785{
786 PDRVNAT pThis = (PDRVNAT)pvUser;
787 Assert(pThis);
788 TMTimerSetMillies(pThis->pTmrSlow, 500);
789}
790
791/**
792 * Function called by slirp to check if it's possible to feed incoming data to the network port.
793 * @returns 1 if possible.
794 * @returns 0 if not possible.
795 */
796int slirp_can_output(void *pvUser)
797{
798 return 1;
799}
800
801void slirp_push_recv_thread(void *pvUser)
802{
803 PDRVNAT pThis = (PDRVNAT)pvUser;
804 Assert(pThis);
805 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
806}
807
808void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
809{
810 PDRVNAT pThis = (PDRVNAT)pvUser;
811 Assert(pThis);
812
813 PRTREQ pReq = NULL;
814
815 /* don't queue new requests when the NAT thread is about to stop */
816 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
817 return;
818
819 ASMAtomicIncU32(&pThis->cUrgPkt);
820 int rc = RTReqCallEx(pThis->pUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
821 (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
822 AssertRC(rc);
823 drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
824}
825
826/**
827 * Function called by slirp to feed incoming data to the NIC.
828 */
829void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
830{
831 PDRVNAT pThis = (PDRVNAT)pvUser;
832 Assert(pThis);
833
834 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
835 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
836
837 PRTREQ pReq = NULL;
838
839 /* don't queue new requests when the NAT thread is about to stop */
840 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
841 return;
842
843 ASMAtomicIncU32(&pThis->cPkt);
844 int rc = RTReqCallEx(pThis->pRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
845 (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
846 AssertRC(rc);
847 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
848 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
849}
850
851
852/**
853 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
854 */
855static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
856{
857 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
858 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
859
860 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
861 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
862 return NULL;
863}
864
865
866/**
867 * Get the MAC address into the slirp stack.
868 *
869 * Called by drvNATLoadDone and drvNATPowerOn.
870 */
871static void drvNATSetMac(PDRVNAT pThis)
872{
873 if (pThis->pIAboveConfig)
874 {
875 RTMAC Mac;
876 pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
877 /* Re-activate the port forwarding. If */
878 slirp_set_ethaddr_and_activate_port_forwarding(pThis->pNATState, Mac.au8, pThis->GuestIP);
879 }
880}
881
882
883/**
884 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
885 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
886 * (usually done during guest boot).
887 */
888static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
889{
890 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
891 drvNATSetMac(pThis);
892 return VINF_SUCCESS;
893}
894
895
896/**
897 * Some guests might not use DHCP to retrieve an IP but use a static IP.
898 */
899static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
900{
901 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
902 drvNATSetMac(pThis);
903}
904
905
906/**
907 * Sets up the redirectors.
908 *
909 * @returns VBox status code.
910 * @param pCfg The configuration handle.
911 */
912static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, RTIPV4ADDR Network)
913{
914 RTMAC Mac;
915 memset(&Mac, 0, sizeof(RTMAC)); /*can't get MAC here */
916 /*
917 * Enumerate redirections.
918 */
919 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfg); pNode; pNode = CFGMR3GetNextChild(pNode))
920 {
921 /*
922 * Validate the port forwarding config.
923 */
924 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
925 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
926
927 /* protocol type */
928 bool fUDP;
929 char szProtocol[32];
930 int rc;
931 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
932 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
933 {
934 fUDP = false;
935 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
936 }
937 else if (RT_SUCCESS(rc))
938 {
939 if (!RTStrICmp(szProtocol, "TCP"))
940 fUDP = false;
941 else if (!RTStrICmp(szProtocol, "UDP"))
942 fUDP = true;
943 else
944 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
945 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
946 iInstance, szProtocol);
947 }
948 /* host port */
949 int32_t iHostPort;
950 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
951
952 /* guest port */
953 int32_t iGuestPort;
954 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
955
956 /* guest address */
957 struct in_addr GuestIP;
958 /* @todo (vvl) use CTL_* */
959 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
960
961 /* Store the guest IP for re-establishing the port-forwarding rules. Note that GuestIP
962 * is not documented. Without */
963 if (pThis->GuestIP == INADDR_ANY)
964 pThis->GuestIP = GuestIP.s_addr;
965
966 /*
967 * Call slirp about it.
968 */
969 struct in_addr BindIP;
970 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
971 if (slirp_redir(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
972 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
973 N_("NAT#%d: configuration error: failed to set up "
974 "redirection of %d to %d. Probably a conflict with "
975 "existing services or other rules"), iInstance, iHostPort,
976 iGuestPort);
977 } /* for each redir rule */
978
979 return VINF_SUCCESS;
980}
981
982
983/**
984 * Destruct a driver instance.
985 *
986 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
987 * resources can be freed correctly.
988 *
989 * @param pDrvIns The driver instance data.
990 */
991static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
992{
993 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
994 LogFlow(("drvNATDestruct:\n"));
995 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
996
997 if (pThis->pNATState)
998 {
999 slirp_term(pThis->pNATState);
1000 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
1001#ifdef VBOX_WITH_STATISTICS
1002# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1003# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1004# include "counters.h"
1005#endif
1006 pThis->pNATState = NULL;
1007 }
1008}
1009
1010
1011/**
1012 * Construct a NAT network transport driver instance.
1013 *
1014 * @copydoc FNPDMDRVCONSTRUCT
1015 */
1016static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1017{
1018 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1019 LogFlow(("drvNATConstruct:\n"));
1020 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1021
1022 /*
1023 * Validate the config.
1024 */
1025 if (!CFGMR3AreValuesValid(pCfg,
1026 "PassDomain\0TFTPPrefix\0BootFile\0Network"
1027 "\0NextServer\0DNSProxy\0BindIP\0UseHostResolver\0"
1028 "SlirpMTU\0"
1029 "SockRcv\0SockSnd\0TcpRcv\0TcpSnd\0"))
1030 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
1031 N_("Unknown NAT configuration option, only supports PassDomain,"
1032 " TFTPPrefix, BootFile and Network"));
1033
1034 /*
1035 * Init the static parts.
1036 */
1037 pThis->pDrvIns = pDrvIns;
1038 pThis->pNATState = NULL;
1039 pThis->pszTFTPPrefix = NULL;
1040 pThis->pszBootFile = NULL;
1041 pThis->pszNextServer = NULL;
1042 /* IBase */
1043 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1044 /* INetwork */
1045 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1046 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1047 pThis->INetworkUp.pfnSendDeprecated = drvNATNetworkUp_SendDeprecated;
1048 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1049 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1050
1051 /*
1052 * Get the configuration settings.
1053 */
1054 int rc;
1055 bool fPassDomain = true;
1056 GET_BOOL(rc, pThis, pCfg, "PassDomain", fPassDomain);
1057
1058 GET_STRING_ALLOC(rc, pThis, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1059 GET_STRING_ALLOC(rc, pThis, pCfg, "BootFile", pThis->pszBootFile);
1060 GET_STRING_ALLOC(rc, pThis, pCfg, "NextServer", pThis->pszNextServer);
1061
1062 int fDNSProxy = 0;
1063 GET_S32(rc, pThis, pCfg, "DNSProxy", fDNSProxy);
1064 int fUseHostResolver = 0;
1065 GET_S32(rc, pThis, pCfg, "UseHostResolver", fUseHostResolver);
1066#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1067 int MTU = 1500;
1068 GET_S32(rc, pThis, pCfg, "SlirpMTU", MTU);
1069#endif
1070
1071 /*
1072 * Query the network port interface.
1073 */
1074 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1075 if (!pThis->pIAboveNet)
1076 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1077 N_("Configuration error: the above device/driver didn't "
1078 "export the network port interface"));
1079 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1080 if (!pThis->pIAboveConfig)
1081 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1082 N_("Configuration error: the above device/driver didn't "
1083 "export the network config interface"));
1084
1085 /* Generate a network address for this network card. */
1086 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1087 GET_STRING(rc, pThis, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1088 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1089 RTStrPrintf(szNetwork, sizeof(szNetwork), "10.0.%d.0/24", pDrvIns->iInstance + 2);
1090
1091 RTIPV4ADDR Network;
1092 RTIPV4ADDR Netmask;
1093 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1094 if (RT_FAILURE(rc))
1095 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
1096 "network '%s' describes not a valid IPv4 network"),
1097 pDrvIns->iInstance, szNetwork);
1098
1099 char szNetAddr[16];
1100 RTStrPrintf(szNetAddr, sizeof(szNetAddr), "%d.%d.%d.%d",
1101 (Network & 0xFF000000) >> 24, (Network & 0xFF0000) >> 16,
1102 (Network & 0xFF00) >> 8, Network & 0xFF);
1103
1104 /*
1105 * Initialize slirp.
1106 */
1107 rc = slirp_init(&pThis->pNATState, &szNetAddr[0], Netmask, fPassDomain, !!fUseHostResolver, pThis);
1108 if (RT_SUCCESS(rc))
1109 {
1110 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
1111 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
1112 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
1113 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
1114#ifdef VBOX_WITH_SLIRP_BSD_MBUF
1115 slirp_set_mtu(pThis->pNATState, MTU);
1116#endif
1117 char *pszBindIP = NULL;
1118 GET_STRING_ALLOC(rc, pThis, pCfg, "BindIP", pszBindIP);
1119 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
1120 if (rc != 0)
1121 LogRel(("NAT: value of BindIP has been ignored\n"));
1122
1123 if(pszBindIP != NULL)
1124 MMR3HeapFree(pszBindIP);
1125#define SLIRP_SET_TUNING_VALUE(name, setter) \
1126 do \
1127 { \
1128 int len = 0; \
1129 rc = CFGMR3QueryS32(pCfg, name, &len); \
1130 if (RT_SUCCESS(rc)) \
1131 setter(pThis->pNATState, len); \
1132 } while(0)
1133
1134 SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
1135 SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
1136 SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
1137 SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
1138
1139 slirp_register_statistics(pThis->pNATState, pDrvIns);
1140#ifdef VBOX_WITH_STATISTICS
1141# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1142# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1143# include "counters.h"
1144#endif
1145
1146 int rc2 = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, Network);
1147 if (RT_SUCCESS(rc2))
1148 {
1149 /*
1150 * Register a load done notification to get the MAC address into the slirp
1151 * engine after we loaded a guest state.
1152 */
1153 rc2 = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
1154 AssertRC(rc2);
1155 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
1156 if (RT_FAILURE(rc))
1157 {
1158 LogRel(("NAT: Can't create request queue\n"));
1159 return rc;
1160 }
1161
1162
1163 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1164 if (RT_FAILURE(rc))
1165 {
1166 LogRel(("NAT: Can't create request queue\n"));
1167 return rc;
1168 }
1169 rc = RTReqCreateQueue(&pThis->pUrgRecvReqQueue);
1170 if (RT_FAILURE(rc))
1171 {
1172 LogRel(("NAT: Can't create request queue\n"));
1173 return rc;
1174 }
1175 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1176 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1177 AssertRC(rc);
1178 rc = RTSemEventCreate(&pThis->EventRecv);
1179
1180 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1181 drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1182 AssertRC(rc);
1183 rc = RTSemEventCreate(&pThis->EventRecv);
1184 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1185 rc = RTCritSectInit(&pThis->csDevAccess);
1186 rc = PDMDrvHlpTMTimerCreate(pThis->pDrvIns, TMCLOCK_REAL/*enmClock*/, drvNATSlowTimer,
1187 pThis, TMTIMER_FLAGS_NO_CRIT_SECT/*flags*/, "NATSlowTmr", &pThis->pTmrSlow);
1188 rc = PDMDrvHlpTMTimerCreate(pThis->pDrvIns, TMCLOCK_REAL/*enmClock*/, drvNATFastTimer,
1189 pThis, TMTIMER_FLAGS_NO_CRIT_SECT/*flags*/, "NATFastTmr", &pThis->pTmrFast);
1190
1191#ifndef RT_OS_WINDOWS
1192 /*
1193 * Create the control pipe.
1194 */
1195 int fds[2];
1196 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1197 {
1198 rc = RTErrConvertFromErrno(errno);
1199 AssertRC(rc);
1200 return rc;
1201 }
1202 pThis->PipeRead = fds[0];
1203 pThis->PipeWrite = fds[1];
1204#else
1205 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1206 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1207 VBOX_WAKEUP_EVENT_INDEX);
1208#endif
1209
1210 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1211 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1212 AssertRC(rc);
1213
1214#ifdef VBOX_WITH_SLIRP_MT
1215 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1216 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1217 AssertRC(rc);
1218#endif
1219
1220 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
1221
1222 /* might return VINF_NAT_DNS */
1223 return rc;
1224 }
1225 /* failure path */
1226 rc = rc2;
1227 slirp_term(pThis->pNATState);
1228 pThis->pNATState = NULL;
1229 }
1230 else
1231 {
1232 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1233 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1234 }
1235
1236 return rc;
1237}
1238
1239
1240/**
1241 * NAT network transport driver registration record.
1242 */
1243const PDMDRVREG g_DrvNAT =
1244{
1245 /* u32Version */
1246 PDM_DRVREG_VERSION,
1247 /* szName */
1248 "NAT",
1249 /* szRCMod */
1250 "",
1251 /* szR0Mod */
1252 "",
1253 /* pszDescription */
1254 "NAT Network Transport Driver",
1255 /* fFlags */
1256 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1257 /* fClass. */
1258 PDM_DRVREG_CLASS_NETWORK,
1259 /* cMaxInstances */
1260 16,
1261 /* cbInstance */
1262 sizeof(DRVNAT),
1263 /* pfnConstruct */
1264 drvNATConstruct,
1265 /* pfnDestruct */
1266 drvNATDestruct,
1267 /* pfnRelocate */
1268 NULL,
1269 /* pfnIOCtl */
1270 NULL,
1271 /* pfnPowerOn */
1272 drvNATPowerOn,
1273 /* pfnReset */
1274 NULL,
1275 /* pfnSuspend */
1276 NULL,
1277 /* pfnResume */
1278 NULL,
1279 /* pfnAttach */
1280 NULL,
1281 /* pfnDetach */
1282 NULL,
1283 /* pfnPowerOff */
1284 NULL,
1285 /* pfnSoftReset */
1286 NULL,
1287 /* u32EndVersion */
1288 PDM_DRVREG_VERSION
1289};
1290
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