VirtualBox

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

最後變更 在這個檔案從22458是 22458,由 vboxsync 提交於 16 年 前

Fixed bad STAM_PROFILE_STOP usage in drvNATRecv

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 38.3 KB
 
1/* $Id: DrvNAT.cpp 22458 2009-08-26 09:11:26Z vboxsync $ */
2/** @file
3 * DrvNAT - NAT network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 <iprt/assert.h>
33#include <iprt/file.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36#include <iprt/critsect.h>
37#include <iprt/cidr.h>
38#include <iprt/stream.h>
39
40#include "Builtins.h"
41
42#ifndef RT_OS_WINDOWS
43# include <unistd.h>
44# include <fcntl.h>
45# include <poll.h>
46# include <errno.h>
47#endif
48#ifdef RT_OS_FREEBSD
49# include <netinet/in.h>
50#endif
51#include <iprt/semaphore.h>
52#include <iprt/req.h>
53
54#define COUNTERS_INIT
55#include "counters.h"
56
57
58/*******************************************************************************
59* Defined Constants And Macros *
60*******************************************************************************/
61/**
62 * @todo: This is a bad hack to prevent freezing the guest during high network
63 * activity. This needs to be fixed properly.
64 */
65#define VBOX_NAT_DELAY_HACK
66#define SLIRP_SPLIT_CAN_OUTPUT 1
67
68#define GET_EXTRADATA(pthis, node, name, rc, type, type_name, var) \
69do { \
70 (rc) = CFGMR3Query ## type((node), name, &(var)); \
71 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
72 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
73 (pthis)->pDrvIns->iInstance); \
74}while(0)
75
76#define GET_ED_STRICT(pthis, node, name, rc, type, type_name, var) \
77do { \
78 (rc) = CFGMR3Query ## type((node), name, &(var)); \
79 if (RT_FAILURE((rc))) \
80 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
81 (pthis)->pDrvIns->iInstance); \
82}while(0)
83
84#define GET_EXTRADATA_N(pthis, node, name, rc, type, type_name, var, var_size) \
85do { \
86 (rc) = CFGMR3Query ## type((node), name, &(var), var_size); \
87 if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
88 return PDMDrvHlpVMSetError((pthis)->pDrvIns, (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \""name"\" " #type_name " failed"), \
89 (pthis)->pDrvIns->iInstance); \
90}while(0)
91
92#define GET_BOOL(rc, pthis, node, name, var) \
93 GET_EXTRADATA(pthis, node, name, (rc), Bool, bolean, (var))
94#define GET_STRING(rc, pthis, node, name, var, var_size) \
95 GET_EXTRADATA_N(pthis, node, name, (rc), String, string, (var), (var_size))
96#define GET_STRING_ALLOC(rc, pthis, node, name, var) \
97 GET_EXTRADATA(pthis, node, name, (rc), StringAlloc, string, (var))
98#define GET_S32(rc, pthis, node, name, var) \
99 GET_EXTRADATA(pthis, node, name, (rc), S32, int, (var))
100#define GET_S32_STRICT(rc, pthis, node, name, var) \
101 GET_ED_STRICT(pthis, node, name, (rc), S32, int, (var))
102
103
104
105#define DOGETIP(rc, node, instance, status, x) \
106do { \
107 char sz##x[32]; \
108 GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
109 if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
110 (status) = inet_aton(sz ## x, &x); \
111}while(0)
112
113#define GETIP_DEF(rc, node, instance, x, def) \
114do \
115{ \
116 int status = 0; \
117 DOGETIP((rc), (node), (instance), status, x); \
118 if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
119 x.s_addr = def; \
120}while(0)
121
122#define QUEUE_SIZE 50
123
124/*******************************************************************************
125* Structures and Typedefs *
126*******************************************************************************/
127/**
128 * NAT network transport driver instance data.
129 */
130typedef struct DRVNAT
131{
132 /** The network interface. */
133 PDMINETWORKCONNECTOR INetworkConnector;
134 /** The port we're attached to. */
135 PPDMINETWORKPORT pPort;
136 /** The network config of the port we're attached to. */
137 PPDMINETWORKCONFIG pConfig;
138 /** Pointer to the driver instance. */
139 PPDMDRVINS pDrvIns;
140 /** Link state */
141 PDMNETWORKLINKSTATE enmLinkState;
142 /** NAT state for this instance. */
143 PNATState pNATState;
144 /** TFTP directory prefix. */
145 char *pszTFTPPrefix;
146 /** Boot file name to provide in the DHCP server response. */
147 char *pszBootFile;
148 /** tftp server name to provide in the DHCP server response. */
149 char *pszNextServer;
150 /* polling thread */
151 PPDMTHREAD pSlirpThread;
152 /** Queue for NAT-thread-external events. */
153 PRTREQQUEUE pSlirpReqQueue;
154#ifndef SLIRP_SPLIT_CAN_OUTPUT
155 /* Receive PDM queue (deliver packets to the guest) */
156 PPDMQUEUE pRecvQueue;
157#endif
158
159#ifdef VBOX_WITH_SLIRP_MT
160 PPDMTHREAD pGuestThread;
161#endif
162#ifndef RT_OS_WINDOWS
163 /** The write end of the control pipe. */
164 RTFILE PipeWrite;
165 /** The read end of the control pipe. */
166 RTFILE PipeRead;
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#ifdef SLIRP_SPLIT_CAN_OUTPUT
176 /** thread delivering packets for receiving by the guest */
177 PPDMTHREAD pRecvThread;
178 /** event to wakeup the guest receive thread */
179 RTSEMEVENT EventRecv;
180 /** Receive Req queue (deliver packets to the guest) */
181 PRTREQQUEUE pRecvReqQueue;
182#endif
183} DRVNAT;
184/** Pointer the NAT driver instance data. */
185typedef DRVNAT *PDRVNAT;
186
187/**
188 * NAT queue item.
189 */
190typedef struct DRVNATQUEUITEM
191{
192 /** The core part owned by the queue manager. */
193 PDMQUEUEITEMCORE Core;
194 /** The buffer for output to guest. */
195 const uint8_t *pu8Buf;
196 /* size of buffer */
197 size_t cb;
198 void *mbuf;
199} DRVNATQUEUITEM;
200/** Pointer to a NAT queue item. */
201typedef DRVNATQUEUITEM *PDRVNATQUEUITEM;
202
203
204static void drvNATNotifyNATThread(PDRVNAT pThis);
205
206
207/** Converts a pointer to NAT::INetworkConnector to a PRDVNAT. */
208#define PDMINETWORKCONNECTOR_2_DRVNAT(pInterface) ( (PDRVNAT)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAT, INetworkConnector)) )
209
210
211#ifdef SLIRP_SPLIT_CAN_OUTPUT
212
213static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
214 {
215 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
216
217 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
218 return VINF_SUCCESS;
219
220 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
221 {
222 RTReqProcess(pThis->pRecvReqQueue, 0);
223 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
224 }
225 return VINF_SUCCESS;
226}
227
228
229static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
230{
231 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
232 int rc = RTSemEventSignal(pThis->EventRecv);
233
234 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
235 AssertReleaseRC(rc);
236 return VINF_SUCCESS;
237}
238
239
240static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb)
241{
242 STAM_PROFILE_START(&pThis->StatNATRecv, a);
243
244 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
245 int rc = pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, RT_INDEFINITE_WAIT);
246 AssertMsgRC(rc, ("NAT: No RX available even on indefinite wait; rc=%Rrc", rc));
247
248 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
249
250 rc = pThis->pPort->pfnReceive(pThis->pPort, pu8Buf, cb);
251 AssertRC(rc);
252 RTMemFree(pu8Buf);
253
254 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
255}
256
257#endif /* SLIRP_SPLIT_CAN_OUTPUT */
258
259/**
260 * Worker function for drvNATSend().
261 * @thread "NAT" thread.
262 */
263static void drvNATSendWorker(PDRVNAT pThis, const void *pvBuf, size_t cb)
264{
265 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
266 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
267 slirp_input(pThis->pNATState, (uint8_t *)pvBuf, cb);
268}
269
270
271/**
272 * Called by the guest to send data to the network.
273 *
274 * @returns VBox status code.
275 * @param pInterface Pointer to the interface structure containing the called function pointer.
276 * @param pvBuf Data to send.
277 * @param cb Number of bytes to send.
278 * @thread EMT
279 */
280static DECLCALLBACK(int) drvNATSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
281{
282 PDRVNAT pThis = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
283
284 LogFlow(("drvNATSend: pvBuf=%p cb=%#x\n", pvBuf, cb));
285 Log2(("drvNATSend: pvBuf=%p cb=%#x\n%.*Rhxd\n", pvBuf, cb, cb, pvBuf));
286
287 PRTREQ pReq = NULL;
288 int rc;
289 void *buf;
290
291 /* don't queue new requests when the NAT thread is about to stop */
292 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
293 return VINF_SUCCESS;
294
295#ifndef VBOX_WITH_SLIRP_MT
296 rc = RTReqAlloc(pThis->pSlirpReqQueue, &pReq, RTREQTYPE_INTERNAL);
297#else
298 rc = RTReqAlloc((PRTREQQUEUE)slirp_get_queue(pThis->pNATState), &pReq, RTREQTYPE_INTERNAL);
299#endif
300 AssertReleaseRC(rc);
301
302 /* @todo: Here we should get mbuf instead temporal buffer */
303 buf = RTMemAlloc(cb);
304 if (buf == NULL)
305 {
306 LogRel(("NAT: Can't allocate send buffer\n"));
307 return VERR_NO_MEMORY;
308 }
309 memcpy(buf, pvBuf, cb);
310
311 pReq->u.Internal.pfn = (PFNRT)drvNATSendWorker;
312 pReq->u.Internal.cArgs = 3;
313 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
314 pReq->u.Internal.aArgs[1] = (uintptr_t)buf;
315 pReq->u.Internal.aArgs[2] = (uintptr_t)cb;
316 pReq->fFlags = RTREQFLAGS_VOID|RTREQFLAGS_NO_WAIT;
317
318 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
319 AssertReleaseRC(rc);
320 drvNATNotifyNATThread(pThis);
321 LogFlow(("drvNATSend: end\n"));
322 return VINF_SUCCESS;
323}
324
325
326/**
327 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
328 */
329static void drvNATNotifyNATThread(PDRVNAT pThis)
330{
331 int rc;
332#ifndef RT_OS_WINDOWS
333 /* kick select() */
334 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
335#else
336 /* kick WSAWaitForMultipleEvents */
337 rc = WSASetEvent(pThis->hWakeupEvent);
338#endif
339 AssertReleaseRC(rc);
340}
341
342
343/**
344 * Set promiscuous mode.
345 *
346 * This is called when the promiscuous mode is set. This means that there doesn't have
347 * to be a mode change when it's called.
348 *
349 * @param pInterface Pointer to the interface structure containing the called function pointer.
350 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
351 * @thread EMT
352 */
353static DECLCALLBACK(void) drvNATSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
354{
355 LogFlow(("drvNATSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
356 /* nothing to do */
357}
358
359/**
360 * Worker function for drvNATNotifyLinkChanged().
361 * @thread "NAT" thread.
362 */
363static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
364{
365 pThis->enmLinkState = enmLinkState;
366
367 switch (enmLinkState)
368 {
369 case PDMNETWORKLINKSTATE_UP:
370 LogRel(("NAT: link up\n"));
371 slirp_link_up(pThis->pNATState);
372 break;
373
374 case PDMNETWORKLINKSTATE_DOWN:
375 case PDMNETWORKLINKSTATE_DOWN_RESUME:
376 LogRel(("NAT: link down\n"));
377 slirp_link_down(pThis->pNATState);
378 break;
379
380 default:
381 AssertMsgFailed(("drvNATNotifyLinkChanged: unexpected link state %d\n", enmLinkState));
382 }
383}
384
385
386/**
387 * Notification on link status changes.
388 *
389 * @param pInterface Pointer to the interface structure containing the called function pointer.
390 * @param enmLinkState The new link state.
391 * @thread EMT
392 */
393static DECLCALLBACK(void) drvNATNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
394{
395 PDRVNAT pThis = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
396
397 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
398
399 PRTREQ pReq = NULL;
400
401 /* don't queue new requests when the NAT thread is about to stop */
402 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
403 return;
404
405 int rc = RTReqAlloc(pThis->pSlirpReqQueue, &pReq, RTREQTYPE_INTERNAL);
406 AssertReleaseRC(rc);
407 pReq->u.Internal.pfn = (PFNRT)drvNATNotifyLinkChangedWorker;
408 pReq->u.Internal.cArgs = 2;
409 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
410 pReq->u.Internal.aArgs[1] = (uintptr_t)enmLinkState;
411 pReq->fFlags = RTREQFLAGS_VOID;
412 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
413 if (RT_LIKELY(rc == VERR_TIMEOUT))
414 {
415 drvNATNotifyNATThread(pThis);
416 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
417 AssertReleaseRC(rc);
418 }
419 else
420 AssertReleaseRC(rc);
421 RTReqFree(pReq);
422}
423
424/**
425 * NAT thread handling the slirp stuff. The slirp implementation is single-threaded
426 * so we execute this enginre in a dedicated thread. We take care that this thread
427 * does not become the bottleneck: If the guest wants to send, a request is enqueued
428 * into the pSlirpReqQueue and handled asynchronously by this thread. If this thread
429 * wants to deliver packets to the guest, it enqueues a request into pRecvReqQueue
430 * which is later handled by the Recv thread.
431 */
432static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
433{
434 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
435 int nFDs = -1;
436 unsigned int ms;
437#ifdef RT_OS_WINDOWS
438 DWORD event;
439 HANDLE *phEvents;
440 unsigned int cBreak = 0;
441#else /* RT_OS_WINDOWS */
442 struct pollfd *polls = NULL;
443 unsigned int cPollNegRet = 0;
444#endif /* !RT_OS_WINDOWS */
445
446 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
447
448 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
449 return VINF_SUCCESS;
450
451#ifdef RT_OS_WINDOWS
452 phEvents = slirp_get_events(pThis->pNATState);
453#endif /* RT_OS_WINDOWS */
454
455 /*
456 * Polling loop.
457 */
458 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
459 {
460 nFDs = -1;
461 /*
462 * To prevent concurent execution of sending/receving threads
463 */
464#ifndef RT_OS_WINDOWS
465 nFDs = slirp_get_nsock(pThis->pNATState);
466 polls = NULL;
467 /* allocation for all sockets + Management pipe */
468 polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
469 if (polls == NULL)
470 return VERR_NO_MEMORY;
471
472 /* don't pass the managemant pipe */
473 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
474 ms = slirp_get_timeout_ms(pThis->pNATState);
475
476 polls[0].fd = pThis->PipeRead;
477 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
478 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
479 polls[0].revents = 0;
480
481 int cChangedFDs = poll(polls, nFDs + 1, ms ? ms : -1);
482 if (cChangedFDs < 0)
483 {
484 if (errno == EINTR)
485 {
486 Log2(("NAT: signal was caught while sleep on poll\n"));
487 /* No error, just process all outstanding requests but don't wait */
488 cChangedFDs = 0;
489 }
490 else if (cPollNegRet++ > 128)
491 {
492 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
493 cPollNegRet = 0;
494 }
495 }
496
497 if (cChangedFDs >= 0)
498 {
499 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
500 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
501 {
502 /* drain the pipe */
503 char ch[1];
504 size_t cbRead;
505 int counter = 0;
506 /*
507 * drvNATSend decoupled so we don't know how many times
508 * device's thread sends before we've entered multiplex,
509 * so to avoid false alarm drain pipe here to the very end
510 *
511 * @todo: Probably we should counter drvNATSend to count how
512 * deep pipe has been filed before drain.
513 *
514 * XXX:Make it reading exactly we need to drain the pipe.
515 */
516 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
517 }
518 }
519 /* process _all_ outstanding requests but don't wait */
520 RTReqProcess(pThis->pSlirpReqQueue, 0);
521 RTMemFree(polls);
522#else /* RT_OS_WINDOWS */
523 slirp_select_fill(pThis->pNATState, &nFDs);
524 ms = slirp_get_timeout_ms(pThis->pNATState);
525 struct timeval tv = { 0, ms*1000 };
526 event = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE, ms ? ms : WSA_INFINITE, FALSE);
527 if ( (event < WSA_WAIT_EVENT_0 || event > WSA_WAIT_EVENT_0 + nFDs - 1)
528 && event != WSA_WAIT_TIMEOUT)
529 {
530 int error = WSAGetLastError();
531 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", event, error));
532 RTAssertReleasePanic();
533 }
534
535 if (event == WSA_WAIT_TIMEOUT)
536 {
537 /* only check for slow/fast timers */
538 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
539 continue;
540 }
541 /* poll the sockets in any case */
542 Log2(("%s: poll\n", __FUNCTION__));
543 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(event == WSA_WAIT_EVENT_0));
544 /* process _all_ outstanding requests but don't wait */
545 RTReqProcess(pThis->pSlirpReqQueue, 0);
546# ifdef VBOX_NAT_DELAY_HACK
547 if (cBreak++ > 128)
548 {
549 cBreak = 0;
550 RTThreadSleep(2);
551 }
552# endif
553#endif /* RT_OS_WINDOWS */
554 }
555
556 return VINF_SUCCESS;
557}
558
559
560/**
561 * Unblock the send thread so it can respond to a state change.
562 *
563 * @returns VBox status code.
564 * @param pDevIns The pcnet device instance.
565 * @param pThread The send thread.
566 */
567static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
568{
569 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
570
571 drvNATNotifyNATThread(pThis);
572 return VINF_SUCCESS;
573}
574
575#ifdef VBOX_WITH_SLIRP_MT
576
577static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
578{
579 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
580
581 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
582 return VINF_SUCCESS;
583
584 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
585 slirp_process_queue(pThis->pNATState);
586
587 return VINF_SUCCESS;
588}
589
590
591static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
592{
593 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
594
595 return VINF_SUCCESS;
596}
597
598#endif /* VBOX_WITH_SLIRP_MT */
599
600
601/**
602 * Function called by slirp to check if it's possible to feed incoming data to the network port.
603 * @returns 1 if possible.
604 * @returns 0 if not possible.
605 */
606int slirp_can_output(void *pvUser)
607{
608 return 1;
609}
610
611/**
612 * Function called by slirp to feed incoming data to the network port.
613 */
614void slirp_output(void *pvUser, void *pvArg, const uint8_t *pu8Buf, int cb)
615{
616 PDRVNAT pThis = (PDRVNAT)pvUser;
617 Assert(pThis);
618
619 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
620 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
621
622#ifndef SLIRP_SPLIT_CAN_OUTPUT
623 PDRVNATQUEUITEM pItem = (PDRVNATQUEUITEM)PDMQueueAlloc(pThis->pRecvQueue);
624 if (pItem)
625 {
626 pItem->pu8Buf = pu8Buf;
627 pItem->cb = cb;
628 pItem->mbuf = pvArg;
629 Log2(("pItem:%p %.Rhxd\n", pItem, pItem->pu8Buf));
630 PDMQueueInsert(pThis->pRecvQueue, &pItem->Core);
631 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
632 return;
633 }
634 static unsigned s_cDroppedPackets;
635 if (s_cDroppedPackets < 64)
636 s_cDroppedPackets++;
637 else
638 {
639 LogRel(("NAT: %d messages suppressed about dropping packet (couldn't allocate queue item)\n", s_cDroppedPackets));
640 s_cDroppedPackets = 0;
641 }
642 STAM_COUNTER_INC(&pThis->StatQueuePktDropped);
643 RTMemFree((void *)pu8Buf);
644#else
645 PRTREQ pReq = NULL;
646
647 /* don't queue new requests when the NAT thread is about to stop */
648 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
649 return;
650
651 int rc = RTReqAlloc(pThis->pRecvReqQueue, &pReq, RTREQTYPE_INTERNAL);
652 AssertReleaseRC(rc);
653 pReq->u.Internal.pfn = (PFNRT)drvNATRecvWorker;
654 pReq->u.Internal.cArgs = 3;
655 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
656 pReq->u.Internal.aArgs[1] = (uintptr_t)pu8Buf;
657 pReq->u.Internal.aArgs[2] = (uintptr_t)cb;
658 pReq->fFlags = RTREQFLAGS_VOID|RTREQFLAGS_NO_WAIT;
659 rc = RTReqQueue(pReq, 0);
660 AssertReleaseRC(rc);
661 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
662 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
663#endif
664}
665
666
667#ifndef SLIRP_SPLIT_CAN_OUTPUT
668/**
669 * Queue callback for processing a queued item.
670 *
671 * @returns Success indicator.
672 * If false the item will not be removed and the flushing will stop.
673 * @param pDrvIns The driver instance.
674 * @param pItemCore Pointer to the queue item to process.
675 */
676static DECLCALLBACK(bool) drvNATQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItemCore)
677{
678 int rc;
679 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
680 PDRVNATQUEUITEM pItem = (PDRVNATQUEUITEM)pItemCore;
681 PRTREQ pReq = NULL;
682 Log(("drvNATQueueConsumer(pItem:%p, pu8Buf:%p, cb:%d)\n", pItem, pItem->pu8Buf, pItem->cb));
683 Log2(("drvNATQueueConsumer: pu8Buf:\n%.Rhxd\n", pItem->pu8Buf));
684 if (RT_FAILURE(pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, 0)))
685 {
686 STAM_COUNTER_INC(&pThis->StatConsumerFalse);
687 return false;
688 }
689 rc = pThis->pPort->pfnReceive(pThis->pPort, pItem->pu8Buf, pItem->cb);
690 RTMemFree((void *)pItem->pu8Buf);
691 return true;
692
693 AssertRelease(pItem->mbuf == NULL);
694 return RT_SUCCESS(rc);
695}
696#endif
697
698
699/**
700 * Queries an interface to the driver.
701 *
702 * @returns Pointer to interface.
703 * @returns NULL if the interface was not supported by the driver.
704 * @param pInterface Pointer to this interface structure.
705 * @param enmInterface The requested interface identification.
706 * @thread Any thread.
707 */
708static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
709{
710 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
711 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
712 switch (enmInterface)
713 {
714 case PDMINTERFACE_BASE:
715 return &pDrvIns->IBase;
716 case PDMINTERFACE_NETWORK_CONNECTOR:
717 return &pThis->INetworkConnector;
718 default:
719 return NULL;
720 }
721}
722
723
724/**
725 * Get the MAC address into the slirp stack.
726 *
727 * Called by drvNATLoadDone and drvNATPowerOn.
728 */
729static void drvNATSetMac(PDRVNAT pThis)
730{
731 if (pThis->pConfig)
732 {
733 RTMAC Mac;
734 pThis->pConfig->pfnGetMac(pThis->pConfig, &Mac);
735 slirp_set_ethaddr(pThis->pNATState, Mac.au8);
736 }
737}
738
739
740/**
741 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
742 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
743 * (usually done during guest boot).
744 */
745static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
746{
747 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
748 drvNATSetMac(pThis);
749 return VINF_SUCCESS;
750}
751
752
753/**
754 * Some guests might not use DHCP to retrieve an IP but use a static IP.
755 */
756static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
757{
758 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
759 drvNATSetMac(pThis);
760}
761
762
763/**
764 * Sets up the redirectors.
765 *
766 * @returns VBox status code.
767 * @param pCfgHandle The drivers configuration handle.
768 */
769static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfgHandle, RTIPV4ADDR Network)
770{
771 RTMAC Mac;
772 memset(&Mac, 0, sizeof(RTMAC)); /*can't get MAC here */
773 /*
774 * Enumerate redirections.
775 */
776 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfgHandle); pNode; pNode = CFGMR3GetNextChild(pNode))
777 {
778 /*
779 * Validate the port forwarding config.
780 */
781 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
782 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
783
784 /* protocol type */
785 bool fUDP;
786 char szProtocol[32];
787 int rc;
788 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
789 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
790 {
791 fUDP = false;
792 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
793 }
794 else if (RT_SUCCESS(rc))
795 {
796 if (!RTStrICmp(szProtocol, "TCP"))
797 fUDP = false;
798 else if (!RTStrICmp(szProtocol, "UDP"))
799 fUDP = true;
800 else
801 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
802 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
803 iInstance, szProtocol);
804 }
805 /* host port */
806 int32_t iHostPort;
807 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
808
809 /* guest port */
810 int32_t iGuestPort;
811 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
812
813 /* guest address */
814 struct in_addr GuestIP;
815 /* @todo (vvl) use CTL_* */
816 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
817
818 /*
819 * Call slirp about it.
820 */
821 struct in_addr BindIP;
822 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
823 if (slirp_redir(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
824 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
825 N_("NAT#%d: configuration error: failed to set up "
826 "redirection of %d to %d. Probably a conflict with "
827 "existing services or other rules"), iInstance, iHostPort,
828 iGuestPort);
829 } /* for each redir rule */
830
831 return VINF_SUCCESS;
832}
833
834
835/**
836 * Destruct a driver instance.
837 *
838 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
839 * resources can be freed correctly.
840 *
841 * @param pDrvIns The driver instance data.
842 */
843static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
844{
845 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
846
847 LogFlow(("drvNATDestruct:\n"));
848
849 slirp_term(pThis->pNATState);
850 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
851 pThis->pNATState = NULL;
852#ifdef VBOX_WITH_STATISTICS
853# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
854# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
855# include "counters.h"
856#endif
857}
858
859
860/**
861 * Construct a NAT network transport driver instance.
862 *
863 * @copydoc FNPDMDRVCONSTRUCT
864 */
865static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
866{
867 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
868
869 LogFlow(("drvNATConstruct:\n"));
870
871 /*
872 * Validate the config.
873 */
874 if (!CFGMR3AreValuesValid(pCfgHandle,
875 "PassDomain\0TFTPPrefix\0BootFile\0Network"
876 "\0NextServer\0DNSProxy\0BindIP\0"
877 "SocketRcvBuf\0SocketSndBuf\0TcpRcvSpace\0TcpSndSpace\0"))
878 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
879 N_("Unknown NAT configuration option, only supports PassDomain,"
880 " TFTPPrefix, BootFile and Network"));
881
882 /*
883 * Init the static parts.
884 */
885 pThis->pDrvIns = pDrvIns;
886 pThis->pNATState = NULL;
887 pThis->pszTFTPPrefix = NULL;
888 pThis->pszBootFile = NULL;
889 pThis->pszNextServer = NULL;
890 /* IBase */
891 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
892 /* INetwork */
893 pThis->INetworkConnector.pfnSend = drvNATSend;
894 pThis->INetworkConnector.pfnSetPromiscuousMode = drvNATSetPromiscuousMode;
895 pThis->INetworkConnector.pfnNotifyLinkChanged = drvNATNotifyLinkChanged;
896
897 /*
898 * Get the configuration settings.
899 */
900 int rc;
901 bool fPassDomain = true;
902 GET_BOOL(rc, pThis, pCfgHandle, "PassDomain", fPassDomain);
903
904 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "TFTPPrefix", pThis->pszTFTPPrefix);
905 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "BootFile", pThis->pszBootFile);
906 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "NextServer", pThis->pszNextServer);
907
908 int fDNSProxy = 0;
909 GET_S32(rc, pThis, pCfgHandle, "DNSProxy", fDNSProxy);
910
911 /*
912 * Query the network port interface.
913 */
914 pThis->pPort =
915 (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase,
916 PDMINTERFACE_NETWORK_PORT);
917 if (!pThis->pPort)
918 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
919 N_("Configuration error: the above device/driver didn't "
920 "export the network port interface"));
921 pThis->pConfig =
922 (PPDMINETWORKCONFIG)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase,
923 PDMINTERFACE_NETWORK_CONFIG);
924 if (!pThis->pConfig)
925 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
926 N_("Configuration error: the above device/driver didn't "
927 "export the network config interface"));
928
929 /* Generate a network address for this network card. */
930 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
931 GET_STRING(rc, pThis, pCfgHandle, "Network", szNetwork[0], sizeof(szNetwork));
932 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
933 RTStrPrintf(szNetwork, sizeof(szNetwork), "10.0.%d.0/24", pDrvIns->iInstance + 2);
934
935 RTIPV4ADDR Network;
936 RTIPV4ADDR Netmask;
937 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
938 if (RT_FAILURE(rc))
939 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
940 "network '%s' describes not a valid IPv4 network"),
941 pDrvIns->iInstance, szNetwork);
942
943 char szNetAddr[16];
944 RTStrPrintf(szNetAddr, sizeof(szNetAddr), "%d.%d.%d.%d",
945 (Network & 0xFF000000) >> 24, (Network & 0xFF0000) >> 16,
946 (Network & 0xFF00) >> 8, Network & 0xFF);
947
948 /*
949 * Initialize slirp.
950 */
951 rc = slirp_init(&pThis->pNATState, &szNetAddr[0], Netmask, fPassDomain, pThis);
952 if (RT_SUCCESS(rc))
953 {
954 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
955 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
956 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
957 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
958 char *pszBindIP = NULL;
959 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "BindIP", pszBindIP);
960 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
961 if (rc != 0)
962 LogRel(("NAT: value of BindIP has been ignored\n"));
963
964 if(pszBindIP != NULL)
965 MMR3HeapFree(pszBindIP);
966#define SLIRP_SET_TUNING_VALUE(name, setter) \
967 do \
968 { \
969 int len = 0; \
970 rc = CFGMR3QueryS32(pCfgHandle, name, &len); \
971 if (RT_SUCCESS(rc)) \
972 setter(pThis->pNATState, len); \
973 } while(0)
974
975 SLIRP_SET_TUNING_VALUE("SocketRcvBuf", slirp_set_rcvbuf);
976 SLIRP_SET_TUNING_VALUE("SocketSndBuf", slirp_set_sndbuf);
977 SLIRP_SET_TUNING_VALUE("TcpRcvSpace", slirp_set_tcp_rcvspace);
978 SLIRP_SET_TUNING_VALUE("TcpSndSpace", slirp_set_tcp_sndspace);
979
980 slirp_register_statistics(pThis->pNATState, pDrvIns);
981#ifdef VBOX_WITH_STATISTICS
982# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
983# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
984# include "counters.h"
985#endif
986
987 int rc2 = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfgHandle, Network);
988 if (RT_SUCCESS(rc2))
989 {
990 /*
991 * Register a load done notification to get the MAC address into the slirp
992 * engine after we loaded a guest state.
993 */
994 rc2 = PDMDrvHlpSSMRegister(pDrvIns, pDrvIns->pDrvReg->szDriverName,
995 pDrvIns->iInstance, 0, 0,
996 NULL, NULL, NULL, NULL, NULL, drvNATLoadDone);
997 AssertRC(rc2);
998 rc = RTReqCreateQueue(&pThis->pSlirpReqQueue);
999 if (RT_FAILURE(rc))
1000 {
1001 LogRel(("NAT: Can't create request queue\n"));
1002 return rc;
1003 }
1004
1005
1006#ifndef SLIRP_SPLIT_CAN_OUTPUT
1007 rc = PDMDrvHlpPDMQueueCreate(pDrvIns, sizeof(DRVNATQUEUITEM), QUEUE_SIZE, 0,
1008 drvNATQueueConsumer, "NAT", &pThis->pRecvQueue);
1009 if (RT_FAILURE(rc))
1010 {
1011 LogRel(("NAT: Can't create send queue\n"));
1012 return rc;
1013 }
1014#else
1015 rc = RTReqCreateQueue(&pThis->pRecvReqQueue);
1016 if (RT_FAILURE(rc))
1017 {
1018 LogRel(("NAT: Can't create request queue\n"));
1019 return rc;
1020 }
1021 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1022 drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1023 AssertReleaseRC(rc);
1024 rc = RTSemEventCreate(&pThis->EventRecv);
1025#endif
1026
1027#ifndef RT_OS_WINDOWS
1028 /*
1029 * Create the control pipe.
1030 */
1031 int fds[2];
1032 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1033 {
1034 int rc = RTErrConvertFromErrno(errno);
1035 AssertRC(rc);
1036 return rc;
1037 }
1038 pThis->PipeRead = fds[0];
1039 pThis->PipeWrite = fds[1];
1040#else
1041 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1042 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1043 VBOX_WAKEUP_EVENT_INDEX);
1044#endif
1045
1046 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1047 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1048 AssertReleaseRC(rc);
1049
1050#ifdef VBOX_WITH_SLIRP_MT
1051 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1052 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1053 AssertReleaseRC(rc);
1054#endif
1055
1056 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
1057
1058 /* might return VINF_NAT_DNS */
1059 return rc;
1060 }
1061 /* failure path */
1062 rc = rc2;
1063 slirp_term(pThis->pNATState);
1064 pThis->pNATState = NULL;
1065 }
1066 else
1067 {
1068 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1069 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1070 }
1071
1072 return rc;
1073}
1074
1075
1076/**
1077 * NAT network transport driver registration record.
1078 */
1079const PDMDRVREG g_DrvNAT =
1080{
1081 /* u32Version */
1082 PDM_DRVREG_VERSION,
1083 /* szDriverName */
1084 "NAT",
1085 /* pszDescription */
1086 "NAT Network Transport Driver",
1087 /* fFlags */
1088 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1089 /* fClass. */
1090 PDM_DRVREG_CLASS_NETWORK,
1091 /* cMaxInstances */
1092 16,
1093 /* cbInstance */
1094 sizeof(DRVNAT),
1095 /* pfnConstruct */
1096 drvNATConstruct,
1097 /* pfnDestruct */
1098 drvNATDestruct,
1099 /* pfnIOCtl */
1100 NULL,
1101 /* pfnPowerOn */
1102 drvNATPowerOn,
1103 /* pfnReset */
1104 NULL,
1105 /* pfnSuspend */
1106 NULL,
1107 /* pfnResume */
1108 NULL,
1109 /* pfnAttach */
1110 NULL,
1111 /* pfnDetach */
1112 NULL,
1113 /* pfnPowerOff */
1114 NULL,
1115 /* pfnSoftReset */
1116 NULL,
1117 /* u32EndVersion */
1118 PDM_DRVREG_VERSION
1119};
1120
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette