VirtualBox

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

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

DrvNAT: Build fix for FreeBSD. Thanks to Beat Gaetzi

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

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