VirtualBox

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

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

NAT: re-counters

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

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