VirtualBox

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

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

NAT: more readability

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