VirtualBox

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

Last change on this file since 22250 was 22250, checked in by vboxsync, 16 years ago

NAT: counter consumer false

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.3 KB
Line 
1/* $Id: DrvNAT.cpp 22250 2009-08-14 02:08:14Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_NAT
27#define __STDC_LIMIT_MACROS
28#define __STDC_CONSTANT_MACROS
29#include "slirp/libslirp.h"
30#include "slirp/ctl.h"
31#include <VBox/pdmdrv.h>
32#include <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#if 0
64#define SLIRP_SPLIT_CAN_OUTPUT 1
65#endif
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/*******************************************************************************
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 pThread;
149 /** Queue for NAT-thread-external events. */
150 PRTREQQUEUE pReqQueue;
151 /* Send queue */
152 PPDMQUEUE pSendQueue;
153
154 RTSEMEVENT semStatus;
155#ifdef VBOX_WITH_SLIRP_MT
156 PPDMTHREAD pGuestThread;
157#endif
158#ifndef RT_OS_WINDOWS
159 /** The write end of the control pipe. */
160 RTFILE PipeWrite;
161 /** The read end of the control pipe. */
162 RTFILE PipeRead;
163#else
164 /** for external notification */
165 HANDLE hWakeupEvent;
166#endif
167 STAMCOUNTER StatQueuePktSent; /**< counting packet sent via PDM queue */
168 STAMCOUNTER StatQueuePktDropped; /**< counting packet drops by PDM queue */
169 STAMCOUNTER StatConsumerFalse;
170#ifdef SLIRP_SPLIT_CAN_OUTPUT
171 PPDMTHREAD thrNATRx;
172 RTSEMEVENT semNATRx;
173 bool fCanOutput;
174#endif
175} DRVNAT;
176/** Pointer the NAT driver instance data. */
177typedef DRVNAT *PDRVNAT;
178
179#ifdef SLIRP_SPLIT_CAN_OUTPUT
180static DECLCALLBACK(int) drvNATRx(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
181 {
182 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
183 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
184 return VINF_SUCCESS;
185 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
186 {
187 int rc = pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, 0);
188 bool fHaveRxBuffers = RT_SUCCESS(rc);
189
190 if (!pThis->fCanOutput && fHaveRxBuffers)
191 {
192 int rc;
193 pThis->fCanOutput = fHaveRxBuffers;
194 /*we need inform NAT thread*/
195#ifndef RT_OS_WINDOWS
196 /* kick select() */
197 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
198 AssertRC(rc);
199#else
200 /* kick WSAWaitForMultipleEvents */
201 rc = WSASetEvent(pThis->hWakeupEvent);
202 AssertRelease(rc == TRUE);
203#endif
204 }
205 pThis->fCanOutput = fHaveRxBuffers;
206 RTSemEventWait(pThis->semNATRx, RT_INDEFINITE_WAIT);
207 }
208 return VINF_SUCCESS;
209}
210
211
212static DECLCALLBACK(int) drvNATRxWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
213{
214 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
215 int rc = RTSemEventSignal(pThis->semNATRx);
216 AssertReleaseRC(rc);
217 return VINF_SUCCESS;
218}
219#endif
220
221/**
222 * NAT queue item.
223 */
224enum
225{
226 SLIRP_CHECK,
227 SLIRP_SEND
228};
229
230typedef struct DRVNATQUEUITEM
231{
232 /** The core part owned by the queue manager. */
233 PDMQUEUEITEMCORE Core;
234 /** The buffer for output to guest. */
235 const uint8_t *pu8Buf;
236 /* size of buffer */
237 size_t cb;
238 void *mbuf;
239} DRVNATQUEUITEM;
240/** Pointer to a NAT queue item. */
241typedef DRVNATQUEUITEM *PDRVNATQUEUITEM;
242
243/** Converts a pointer to NAT::INetworkConnector to a PRDVNAT. */
244#define PDMINETWORKCONNECTOR_2_DRVNAT(pInterface) ( (PDRVNAT)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAT, INetworkConnector)) )
245
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 * 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 /* don't queue new requests when the NAT thread is about to stop */
279 if (pThis->pThread->enmState != PDMTHREADSTATE_RUNNING)
280 return VINF_SUCCESS;
281#ifndef VBOX_WITH_SLIRP_MT
282 rc = RTReqAlloc(pThis->pReqQueue, &pReq, RTREQTYPE_INTERNAL);
283#else
284 rc = RTReqAlloc((PRTREQQUEUE)slirp_get_queue(pThis->pNATState), &pReq, RTREQTYPE_INTERNAL);
285#endif
286 AssertReleaseRC(rc);
287
288 /* @todo: Here we should get mbuf instead temporal buffer */
289 buf = RTMemAlloc(cb);
290 if (buf == NULL)
291 {
292 LogRel(("NAT: Can't allocate send buffer\n"));
293 return VERR_NO_MEMORY;
294 }
295 memcpy(buf, pvBuf, cb);
296
297 pReq->u.Internal.pfn = (PFNRT)drvNATSendWorker;
298 pReq->u.Internal.cArgs = 3;
299 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
300 pReq->u.Internal.aArgs[1] = (uintptr_t)buf;
301 pReq->u.Internal.aArgs[2] = (uintptr_t)cb;
302 pReq->fFlags = RTREQFLAGS_VOID|RTREQFLAGS_NO_WAIT;
303
304 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
305 AssertReleaseRC(rc);
306#ifndef RT_OS_WINDOWS
307 /* kick select() */
308 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
309 AssertRC(rc);
310#else
311 /* kick WSAWaitForMultipleEvents */
312 rc = WSASetEvent(pThis->hWakeupEvent);
313 AssertRelease(rc == TRUE);
314#endif
315
316 LogFlow(("drvNATSend: end\n"));
317 return VINF_SUCCESS;
318}
319
320
321/**
322 * Set promiscuous mode.
323 *
324 * This is called when the promiscuous mode is set. This means that there doesn't have
325 * to be a mode change when it's called.
326 *
327 * @param pInterface Pointer to the interface structure containing the called function pointer.
328 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
329 * @thread EMT
330 */
331static DECLCALLBACK(void) drvNATSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
332{
333 LogFlow(("drvNATSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
334 /* nothing to do */
335}
336
337/**
338 * Worker function for drvNATNotifyLinkChanged().
339 * @thread "NAT" thread.
340 */
341static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
342{
343 pThis->enmLinkState = enmLinkState;
344
345 switch (enmLinkState)
346 {
347 case PDMNETWORKLINKSTATE_UP:
348 LogRel(("NAT: link up\n"));
349 slirp_link_up(pThis->pNATState);
350 break;
351
352 case PDMNETWORKLINKSTATE_DOWN:
353 case PDMNETWORKLINKSTATE_DOWN_RESUME:
354 LogRel(("NAT: link down\n"));
355 slirp_link_down(pThis->pNATState);
356 break;
357
358 default:
359 AssertMsgFailed(("drvNATNotifyLinkChanged: unexpected link state %d\n", enmLinkState));
360 }
361}
362
363
364/**
365 * Notification on link status changes.
366 *
367 * @param pInterface Pointer to the interface structure containing the called function pointer.
368 * @param enmLinkState The new link state.
369 * @thread EMT
370 */
371static DECLCALLBACK(void) drvNATNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
372{
373 PDRVNAT pThis = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
374
375 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
376
377 PRTREQ pReq = NULL;
378 /* don't queue new requests when the NAT thread is about to stop */
379 if (pThis->pThread->enmState != PDMTHREADSTATE_RUNNING)
380 return;
381 int rc = RTReqAlloc(pThis->pReqQueue, &pReq, RTREQTYPE_INTERNAL);
382 AssertReleaseRC(rc);
383 pReq->u.Internal.pfn = (PFNRT)drvNATNotifyLinkChangedWorker;
384 pReq->u.Internal.cArgs = 2;
385 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
386 pReq->u.Internal.aArgs[1] = (uintptr_t)enmLinkState;
387 pReq->fFlags = RTREQFLAGS_VOID;
388 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
389 if (RT_LIKELY(rc == VERR_TIMEOUT))
390 {
391#ifndef RT_OS_WINDOWS
392 /* kick select() */
393 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
394 AssertRC(rc);
395#else
396 /* kick WSAWaitForMultipleEvents() */
397 rc = WSASetEvent(pThis->hWakeupEvent);
398 AssertRelease(rc == TRUE);
399#endif
400 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
401 AssertReleaseRC(rc);
402 }
403 else
404 AssertReleaseRC(rc);
405 RTReqFree(pReq);
406}
407
408
409static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
410{
411 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
412 int nFDs = -1;
413 unsigned int ms;
414#ifdef RT_OS_WINDOWS
415 DWORD event;
416 HANDLE *phEvents;
417 unsigned int cBreak = 0;
418#else /* RT_OS_WINDOWS */
419 struct pollfd *polls = NULL;
420 unsigned int cPollNegRet = 0;
421#endif /* !RT_OS_WINDOWS */
422
423 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
424
425 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
426 return VINF_SUCCESS;
427
428#ifdef RT_OS_WINDOWS
429 phEvents = slirp_get_events(pThis->pNATState);
430#endif /* RT_OS_WINDOWS */
431
432 /*
433 * Polling loop.
434 */
435 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
436 {
437 nFDs = -1;
438
439 /*
440 * To prevent concurent execution of sending/receving threads
441 */
442#ifndef RT_OS_WINDOWS
443 nFDs = slirp_get_nsock(pThis->pNATState);
444 polls = NULL;
445 /* allocation for all sockets + Management pipe */
446 polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
447 if (polls == NULL)
448 return VERR_NO_MEMORY;
449
450 /* don't pass the managemant pipe */
451 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
452 ms = slirp_get_timeout_ms(pThis->pNATState);
453
454 polls[0].fd = pThis->PipeRead;
455 /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
456 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND;
457 polls[0].revents = 0;
458
459 int cChangedFDs = poll(polls, nFDs + 1, ms ? ms : -1);
460 if (cChangedFDs < 0)
461 {
462 if (errno == EINTR)
463 {
464 Log2(("NAT: signal was caught while sleep on poll\n"));
465 /* No error, just process all outstanding requests but don't wait */
466 cChangedFDs = 0;
467 }
468 else if (cPollNegRet++ > 128)
469 {
470 LogRel(("NAT:Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
471 cPollNegRet = 0;
472 }
473 }
474
475 if (cChangedFDs >= 0)
476 {
477 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
478 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
479 {
480 /* drain the pipe */
481 char ch[1];
482 size_t cbRead;
483 int counter = 0;
484 /*
485 * drvNATSend decoupled so we don't know how many times
486 * device's thread sends before we've entered multiplex,
487 * so to avoid false alarm drain pipe here to the very end
488 *
489 * @todo: Probably we should counter drvNATSend to count how
490 * deep pipe has been filed before drain.
491 *
492 * XXX:Make it reading exactly we need to drain the pipe.
493 */
494 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
495 }
496 }
497 /* process _all_ outstanding requests but don't wait */
498 RTReqProcess(pThis->pReqQueue, 0);
499 RTMemFree(polls);
500#else /* RT_OS_WINDOWS */
501 slirp_select_fill(pThis->pNATState, &nFDs);
502 ms = slirp_get_timeout_ms(pThis->pNATState);
503 struct timeval tv = { 0, ms*1000 };
504 event = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE, ms ? ms : WSA_INFINITE, FALSE);
505 if ( (event < WSA_WAIT_EVENT_0 || event > WSA_WAIT_EVENT_0 + nFDs - 1)
506 && event != WSA_WAIT_TIMEOUT)
507 {
508 int error = WSAGetLastError();
509 LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", event, error));
510 RTAssertReleasePanic();
511 }
512
513 if (event == WSA_WAIT_TIMEOUT)
514 {
515 /* only check for slow/fast timers */
516 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
517 continue;
518 }
519
520 /* poll the sockets in any case */
521 Log2(("%s: poll\n", __FUNCTION__));
522 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(event == WSA_WAIT_EVENT_0));
523 /* process _all_ outstanding requests but don't wait */
524 RTReqProcess(pThis->pReqQueue, 0);
525# ifdef VBOX_NAT_DELAY_HACK
526 if (cBreak++ > 128)
527 {
528 cBreak = 0;
529 RTThreadSleep(2);
530 }
531# endif
532#endif /* RT_OS_WINDOWS */
533#ifdef SLIRP_SPLIT_CAN_OUTPUT
534 drvNATRxWakeup(pThis->pDrvIns, pThis->thrNATRx);
535#endif
536 }
537
538 return VINF_SUCCESS;
539}
540
541
542/**
543 * Unblock the send thread so it can respond to a state change.
544 *
545 * @returns VBox status code.
546 * @param pDevIns The pcnet device instance.
547 * @param pThread The send thread.
548 */
549static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
550{
551 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
552
553#ifndef RT_OS_WINDOWS
554 /* kick select() */
555 int rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
556 AssertRC(rc);
557#else /* !RT_OS_WINDOWS */
558 /* kick WSAWaitForMultipleEvents() */
559 WSASetEvent(pThis->hWakeupEvent);
560#endif /* RT_OS_WINDOWS */
561
562 return VINF_SUCCESS;
563}
564
565#ifdef VBOX_WITH_SLIRP_MT
566
567static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
568{
569 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
570
571 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
572 return VINF_SUCCESS;
573 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
574 {
575 slirp_process_queue(pThis->pNATState);
576 }
577 return VINF_SUCCESS;
578}
579
580
581static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
582{
583 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
584
585 return VINF_SUCCESS;
586}
587
588#endif /* VBOX_WITH_SLIRP_MT */
589
590/**
591 * Function called by slirp to check if it's possible to feed incoming data to the network port.
592 * @returns 1 if possible.
593 * @returns 0 if not possible.
594 */
595int slirp_can_output(void *pvUser)
596{
597#ifdef SLIRP_SPLIT_CAN_OUTPUT
598 return pThis->fCanOutput;
599#else
600 return 1;
601#endif
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
611 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
612 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
613
614 /** @todo r-bird: Why do you reset the counters every time? You won't ever count
615 * higher than ONE then. If you want to record what happened to the last
616 * queued item, use a U8/bool instead to two 64-bit values. */
617 //STAM_COUNTER_RESET(&pThis->StatQueuePktDropped);
618 //STAM_COUNTER_RESET(&pThis->StatQueuePktSent);
619 Assert(pThis);
620
621 PDRVNATQUEUITEM pItem = (PDRVNATQUEUITEM)PDMQueueAlloc(pThis->pSendQueue);
622 if (pItem)
623 {
624 pItem->pu8Buf = pu8Buf;
625 pItem->cb = cb;
626 pItem->mbuf = pvArg;
627 Log2(("pItem:%p %.Rhxd\n", pItem, pItem->pu8Buf));
628 PDMQueueInsert(pThis->pSendQueue, &pItem->Core);
629 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
630 return;
631 }
632 static unsigned s_cDroppedPackets;
633 if (s_cDroppedPackets < 64)
634 s_cDroppedPackets++;
635 else
636 {
637 LogRel(("NAT: %d messages suppressed about dropping packet (couldn't allocate queue item)\n", s_cDroppedPackets));
638 s_cDroppedPackets = 0;
639 }
640 STAM_COUNTER_INC(&pThis->StatQueuePktDropped);
641 RTMemFree((void *)pu8Buf);
642}
643
644
645/**
646 * Queue callback for processing a queued item.
647 *
648 * @returns Success indicator.
649 * If false the item will not be removed and the flushing will stop.
650 * @param pDrvIns The driver instance.
651 * @param pItemCore Pointer to the queue item to process.
652 */
653static DECLCALLBACK(bool) drvNATQueueConsumer(PPDMDRVINS pDrvIns, PPDMQUEUEITEMCORE pItemCore)
654{
655 int rc;
656 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
657 PDRVNATQUEUITEM pItem = (PDRVNATQUEUITEM)pItemCore;
658 PRTREQ pReq = NULL;
659 Log(("drvNATQueueConsumer(pItem:%p, pu8Buf:%p, cb:%d)\n", pItem, pItem->pu8Buf, pItem->cb));
660 Log2(("drvNATQueueConsumer: pu8Buf:\n%.Rhxd\n", pItem->pu8Buf));
661#ifndef SLIRP_SPLIT_CAN_OUTPUT
662 if (RT_FAILURE(pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, 0)))
663 {
664 STAM_COUNTER_INC(&pThis->StatConsumerFalse);
665 return false;
666 }
667#endif
668 rc = pThis->pPort->pfnReceive(pThis->pPort, pItem->pu8Buf, pItem->cb);
669 RTMemFree((void *)pItem->pu8Buf);
670 return true;
671
672 AssertRelease(pItem->mbuf == NULL);
673 return RT_SUCCESS(rc);
674}
675
676
677/**
678 * Queries an interface to the driver.
679 *
680 * @returns Pointer to interface.
681 * @returns NULL if the interface was not supported by the driver.
682 * @param pInterface Pointer to this interface structure.
683 * @param enmInterface The requested interface identification.
684 * @thread Any thread.
685 */
686static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
687{
688 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
689 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
690 switch (enmInterface)
691 {
692 case PDMINTERFACE_BASE:
693 return &pDrvIns->IBase;
694 case PDMINTERFACE_NETWORK_CONNECTOR:
695 return &pThis->INetworkConnector;
696 default:
697 return NULL;
698 }
699}
700
701
702/**
703 * Get the MAC address into the slirp stack.
704 *
705 * Called by drvNATLoadDone and drvNATPowerOn.
706 */
707static void drvNATSetMac(PDRVNAT pThis)
708{
709 if (pThis->pConfig)
710 {
711 RTMAC Mac;
712 pThis->pConfig->pfnGetMac(pThis->pConfig, &Mac);
713 slirp_set_ethaddr(pThis->pNATState, Mac.au8);
714 }
715}
716
717
718/**
719 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
720 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
721 * (usually done during guest boot).
722 */
723static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
724{
725 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
726 drvNATSetMac(pThis);
727 return VINF_SUCCESS;
728}
729
730
731/**
732 * Some guests might not use DHCP to retrieve an IP but use a static IP.
733 */
734static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
735{
736 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
737 drvNATSetMac(pThis);
738}
739
740
741/**
742 * Sets up the redirectors.
743 *
744 * @returns VBox status code.
745 * @param pCfgHandle The drivers configuration handle.
746 */
747static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfgHandle, RTIPV4ADDR Network)
748{
749 RTMAC Mac;
750 memset(&Mac, 0, sizeof(RTMAC)); /*can't get MAC here */
751 /*
752 * Enumerate redirections.
753 */
754 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfgHandle); pNode; pNode = CFGMR3GetNextChild(pNode))
755 {
756 /*
757 * Validate the port forwarding config.
758 */
759 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
760 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
761
762 /* protocol type */
763 bool fUDP;
764 char szProtocol[32];
765 int rc;
766 GET_STRING(rc, pThis, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
767 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
768 {
769 fUDP = false;
770 GET_BOOL(rc, pThis, pNode, "UDP", fUDP);
771 }
772 else if (RT_SUCCESS(rc))
773 {
774 if (!RTStrICmp(szProtocol, "TCP"))
775 fUDP = false;
776 else if (!RTStrICmp(szProtocol, "UDP"))
777 fUDP = true;
778 else
779 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
780 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
781 iInstance, szProtocol);
782 }
783 /* host port */
784 int32_t iHostPort;
785 GET_S32_STRICT(rc, pThis, pNode, "HostPort", iHostPort);
786
787 /* guest port */
788 int32_t iGuestPort;
789 GET_S32_STRICT(rc, pThis, pNode, "GuestPort", iGuestPort);
790
791 /* guest address */
792 struct in_addr GuestIP;
793 /* @todo (vvl) use CTL_* */
794 GETIP_DEF(rc, pThis, pNode, GuestIP, htonl(Network | CTL_GUEST));
795
796 /*
797 * Call slirp about it.
798 */
799 struct in_addr BindIP;
800 GETIP_DEF(rc, pThis, pNode, BindIP, INADDR_ANY);
801 if (slirp_redir(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort, Mac.au8) < 0)
802 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
803 N_("NAT#%d: configuration error: failed to set up "
804 "redirection of %d to %d. Probably a conflict with "
805 "existing services or other rules"), iInstance, iHostPort,
806 iGuestPort);
807 } /* for each redir rule */
808
809 return VINF_SUCCESS;
810}
811
812
813/**
814 * Destruct a driver instance.
815 *
816 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
817 * resources can be freed correctly.
818 *
819 * @param pDrvIns The driver instance data.
820 */
821static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
822{
823 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
824
825 LogFlow(("drvNATDestruct:\n"));
826
827 slirp_term(pThis->pNATState);
828 slirp_deregister_statistics(pThis->pNATState, pDrvIns);
829 pThis->pNATState = NULL;
830#ifdef VBOX_WITH_STATISTICS
831 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueuePktSent);
832 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatQueuePktDropped);
833 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatConsumerFalse);
834#endif
835}
836
837
838/**
839 * Construct a NAT network transport driver instance.
840 *
841 * @returns VBox status.
842 * @param pDrvIns The driver instance data.
843 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
844 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
845 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
846 * iInstance it's expected to be used a bit in this function.
847 */
848static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
849{
850 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
851 char szNetAddr[16];
852 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
853
854 LogFlow(("drvNATConstruct:\n"));
855
856 /*
857 * Validate the config.
858 */
859 if (!CFGMR3AreValuesValid(pCfgHandle, "PassDomain\0TFTPPrefix\0BootFile\0Network"
860 "\0NextServer\0DNSProxy\0BindIP\0"
861 "SocketRcvBuf\0SocketSndBuf\0TcpRcvSpace\0TcpSndSpace\0"))
862 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
863 N_("Unknown NAT configuration option, only supports PassDomain,"
864 " TFTPPrefix, BootFile and Network"));
865
866 /*
867 * Init the static parts.
868 */
869 pThis->pDrvIns = pDrvIns;
870 pThis->pNATState = NULL;
871 pThis->pszTFTPPrefix = NULL;
872 pThis->pszBootFile = NULL;
873 pThis->pszNextServer = NULL;
874 /* IBase */
875 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
876 /* INetwork */
877 pThis->INetworkConnector.pfnSend = drvNATSend;
878 pThis->INetworkConnector.pfnSetPromiscuousMode = drvNATSetPromiscuousMode;
879 pThis->INetworkConnector.pfnNotifyLinkChanged = drvNATNotifyLinkChanged;
880
881 /*
882 * Get the configuration settings.
883 */
884 int rc;
885 bool fPassDomain = true;
886 GET_BOOL(rc, pThis, pCfgHandle, "PassDomain", fPassDomain);
887
888 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "TFTPPrefix", pThis->pszTFTPPrefix);
889 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "BootFile", pThis->pszBootFile);
890 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "NextServer", pThis->pszNextServer);
891
892 int fDNSProxy = 0;
893 GET_S32(rc, pThis, pCfgHandle, "DNSProxy", fDNSProxy);
894
895 /*
896 * Query the network port interface.
897 */
898 pThis->pPort =
899 (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase,
900 PDMINTERFACE_NETWORK_PORT);
901 if (!pThis->pPort)
902 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
903 N_("Configuration error: the above device/driver didn't "
904 "export the network port interface"));
905 pThis->pConfig =
906 (PPDMINETWORKCONFIG)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase,
907 PDMINTERFACE_NETWORK_CONFIG);
908 if (!pThis->pConfig)
909 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
910 N_("Configuration error: the above device/driver didn't "
911 "export the network config interface"));
912
913 /* Generate a network address for this network card. */
914 GET_STRING(rc, pThis, pCfgHandle, "Network", szNetwork[0], sizeof(szNetwork));
915 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
916 RTStrPrintf(szNetwork, sizeof(szNetwork), "10.0.%d.0/24", pDrvIns->iInstance + 2);
917
918 RTIPV4ADDR Network;
919 RTIPV4ADDR Netmask;
920 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
921 if (RT_FAILURE(rc))
922 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: "
923 "network '%s' describes not a valid IPv4 network"),
924 pDrvIns->iInstance, szNetwork);
925
926 RTStrPrintf(szNetAddr, sizeof(szNetAddr), "%d.%d.%d.%d",
927 (Network & 0xFF000000) >> 24, (Network & 0xFF0000) >> 16,
928 (Network & 0xFF00) >> 8, Network & 0xFF);
929
930 /*
931 * Initialize slirp.
932 */
933 rc = slirp_init(&pThis->pNATState, &szNetAddr[0], Netmask, fPassDomain, pThis);
934 if (RT_SUCCESS(rc))
935 {
936 slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
937 slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
938 slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
939 slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
940 char *pszBindIP = NULL;
941 GET_STRING_ALLOC(rc, pThis, pCfgHandle, "BindIP", pszBindIP);
942 rc = slirp_set_binding_address(pThis->pNATState, pszBindIP);
943 if (rc != 0)
944 LogRel(("NAT: value of BindIP has been ignored\n"));
945
946 if(pszBindIP != NULL)
947 MMR3HeapFree(pszBindIP);
948#define SLIRP_SET_TUNING_VALUE(name, setter) \
949 do \
950 { \
951 int len = 0; \
952 rc = CFGMR3QueryS32(pCfgHandle, name, &len); \
953 if (RT_SUCCESS(rc)) \
954 setter(pThis->pNATState, len); \
955 } while(0)
956
957 SLIRP_SET_TUNING_VALUE("SocketRcvBuf", slirp_set_rcvbuf);
958 SLIRP_SET_TUNING_VALUE("SocketSndBuf", slirp_set_sndbuf);
959 SLIRP_SET_TUNING_VALUE("TcpRcvSpace", slirp_set_tcp_rcvspace);
960 SLIRP_SET_TUNING_VALUE("TcpSndSpace", slirp_set_tcp_sndspace);
961
962 slirp_register_statistics(pThis->pNATState, pDrvIns);
963#ifdef VBOX_WITH_STATISTICS
964 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueuePktSent, STAMTYPE_COUNTER,
965 STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "counting packet sent viai "
966 "PDM queue", "/Drivers/NAT%u/QueuePacketSent", pDrvIns->iInstance);
967 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatQueuePktDropped, STAMTYPE_COUNTER,
968 STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "counting packet sent via PDM"
969 " queue", "/Drivers/NAT%u/QueuePacketDropped", pDrvIns->iInstance);
970 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatConsumerFalse, STAMTYPE_COUNTER,
971 STAMVISIBILITY_ALWAYS, STAMUNIT_COUNT, "counting PDM consumer false"
972 " queue", "/Drivers/NAT%u/QueuePacketDropped", pDrvIns->iInstance);
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->pReqQueue);
987 if (RT_FAILURE(rc))
988 {
989 LogRel(("NAT: Can't create request queue\n"));
990 return rc;
991 }
992
993 rc = PDMDrvHlpPDMQueueCreate(pDrvIns, sizeof(DRVNATQUEUITEM), 50, 0,
994 drvNATQueueConsumer, "NAT", &pThis->pSendQueue);
995 if (RT_FAILURE(rc))
996 {
997 LogRel(("NAT: Can't create send queue\n"));
998 return rc;
999 }
1000
1001 rc = RTSemEventCreate(&pThis->semStatus);
1002 AssertRC(rc);
1003#ifdef SLIRP_SPLIT_CAN_OUTPUT
1004 pThis->fCanOutput = false;
1005 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->thrNATRx, pThis, drvNATRx,
1006 drvNATRxWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
1007 AssertReleaseRC(rc);
1008 rc = RTSemEventCreate(&pThis->semNATRx);
1009#endif
1010
1011#ifndef RT_OS_WINDOWS
1012 /*
1013 * Create the control pipe.
1014 */
1015 int fds[2];
1016 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
1017 {
1018 int rc = RTErrConvertFromErrno(errno);
1019 AssertRC(rc);
1020 return rc;
1021 }
1022 pThis->PipeRead = fds[0];
1023 pThis->PipeWrite = fds[1];
1024#else
1025 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1026 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
1027 VBOX_WAKEUP_EVENT_INDEX);
1028#endif
1029
1030 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pThread, pThis, drvNATAsyncIoThread,
1031 drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
1032 AssertReleaseRC(rc);
1033
1034#ifdef VBOX_WITH_SLIRP_MT
1035 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest,
1036 drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
1037 AssertReleaseRC(rc);
1038#endif
1039
1040 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
1041
1042 /* might return VINF_NAT_DNS */
1043 return rc;
1044 }
1045 /* failure path */
1046 rc = rc2;
1047 slirp_term(pThis->pNATState);
1048 pThis->pNATState = NULL;
1049 }
1050 else
1051 {
1052 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
1053 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
1054 }
1055
1056 return rc;
1057}
1058
1059
1060/**
1061 * NAT network transport driver registration record.
1062 */
1063const PDMDRVREG g_DrvNAT =
1064{
1065 /* u32Version */
1066 PDM_DRVREG_VERSION,
1067 /* szDriverName */
1068 "NAT",
1069 /* pszDescription */
1070 "NAT Network Transport Driver",
1071 /* fFlags */
1072 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1073 /* fClass. */
1074 PDM_DRVREG_CLASS_NETWORK,
1075 /* cMaxInstances */
1076 16,
1077 /* cbInstance */
1078 sizeof(DRVNAT),
1079 /* pfnConstruct */
1080 drvNATConstruct,
1081 /* pfnDestruct */
1082 drvNATDestruct,
1083 /* pfnIOCtl */
1084 NULL,
1085 /* pfnPowerOn */
1086 drvNATPowerOn,
1087 /* pfnReset */
1088 NULL,
1089 /* pfnSuspend */
1090 NULL,
1091 /* pfnResume */
1092 NULL,
1093 /* pfnDetach */
1094 NULL,
1095 /* pfnPowerOff */
1096 NULL
1097};
Note: See TracBrowser for help on using the repository browser.

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