VirtualBox

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

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

NAT: readability

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