VirtualBox

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

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

NAT: Backed r51387

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

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