VirtualBox

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

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

NAT: select => poll (2x speed up on Unix)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/** @file
2 *
3 * VBox network devices:
4 * NAT network transport driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_NAT
28#define __STDC_LIMIT_MACROS
29#define __STDC_CONSTANT_MACROS
30#include "Network/slirp/libslirp.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#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
43# ifndef RT_OS_WINDOWS
44# include <unistd.h>
45# include <poll.h>
46# endif
47# include <errno.h>
48# include <iprt/semaphore.h>
49# include <iprt/req.h>
50#endif
51
52/**
53 * @todo: This is a bad hack to prevent freezing the guest during high network
54 * activity. This needs to be fixed properly.
55 */
56/*#define VBOX_NAT_DELAY_HACK*/
57
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62/**
63 * NAT network transport driver instance data.
64 */
65typedef struct DRVNAT
66{
67 /** The network interface. */
68 PDMINETWORKCONNECTOR INetworkConnector;
69 /** The port we're attached to. */
70 PPDMINETWORKPORT pPort;
71 /** The network config of the port we're attached to. */
72 PPDMINETWORKCONFIG pConfig;
73 /** Pointer to the driver instance. */
74 PPDMDRVINS pDrvIns;
75#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
76 /** Slirp critical section. */
77 RTCRITSECT CritSect;
78#endif
79 /** Link state */
80 PDMNETWORKLINKSTATE enmLinkState;
81 /** NAT state for this instance. */
82 PNATState pNATState;
83 /** TFTP directory prefix. */
84 char *pszTFTPPrefix;
85 /** Boot file name to provide in the DHCP server response. */
86 char *pszBootFile;
87#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
88 /* polling thread */
89 PPDMTHREAD pThread;
90 /** Queue for NAT-thread-external events. */
91 PRTREQQUEUE pReqQueue;
92 /* Send queue */
93 PPDMQUEUE pSendQueue;
94# ifdef VBOX_WITH_SLIRP_MT
95 PPDMTHREAD pGuestThread;
96# endif
97# ifndef RT_OS_WINDOWS
98 /** The write end of the control pipe. */
99 RTFILE PipeWrite;
100 /** The read end of the control pipe. */
101 RTFILE PipeRead;
102# else
103 /** for external notification */
104 HANDLE hWakeupEvent;
105# endif
106#endif
107} DRVNAT, *PDRVNAT;
108
109typedef struct DRVNATQUEUITEM
110{
111 /** The core part owned by the queue manager. */
112 PDMQUEUEITEMCORE Core;
113 /** The buffer for output to guest. */
114 const uint8_t *pu8Buf;
115 /* size of buffer */
116 size_t cb;
117 void *mbuf;
118} DRVNATQUEUITEM, *PDRVNATQUEUITEM;
119
120/** Converts a pointer to NAT::INetworkConnector to a PRDVNAT. */
121#define PDMINETWORKCONNECTOR_2_DRVNAT(pInterface) ( (PDRVNAT)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAT, INetworkConnector)) )
122
123
124/**
125 * Worker function for drvNATSend().
126 * @thread "NAT" thread.
127 */
128static void drvNATSendWorker(PDRVNAT pThis, const void *pvBuf, size_t cb)
129{
130 Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
131 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
132 slirp_input(pThis->pNATState, (uint8_t *)pvBuf, cb);
133}
134
135/**
136 * Send data to the network.
137 *
138 * @returns VBox status code.
139 * @param pInterface Pointer to the interface structure containing the called function pointer.
140 * @param pvBuf Data to send.
141 * @param cb Number of bytes to send.
142 * @thread EMT
143 */
144static DECLCALLBACK(int) drvNATSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
145{
146 PDRVNAT pThis = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
147
148 LogFlow(("drvNATSend: pvBuf=%p cb=%#x\n", pvBuf, cb));
149 Log2(("drvNATSend: pvBuf=%p cb=%#x\n%.*Rhxd\n", pvBuf, cb, cb, pvBuf));
150
151#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
152
153 PRTREQ pReq = NULL;
154 int rc;
155 void *buf;
156 /* don't queue new requests when the NAT thread is about to stop */
157 if (pThis->pThread->enmState != PDMTHREADSTATE_RUNNING)
158 return VINF_SUCCESS;
159# ifndef VBOX_WITH_SLIRP_MT
160 rc = RTReqAlloc(pThis->pReqQueue, &pReq, RTREQTYPE_INTERNAL);
161# else
162 rc = RTReqAlloc((PRTREQQUEUE)slirp_get_queue(pThis->pNATState), &pReq, RTREQTYPE_INTERNAL);
163# endif
164 AssertReleaseRC(rc);
165
166 /* @todo: Here we should get mbuf instead temporal buffer */
167 buf = RTMemAlloc(cb);
168 if (buf == NULL)
169 {
170 LogRel(("Can't allocate buffer for sending buffer\n"));
171 return VERR_NO_MEMORY;
172 }
173 memcpy(buf, pvBuf, cb);
174
175 pReq->u.Internal.pfn = (PFNRT)drvNATSendWorker;
176 pReq->u.Internal.cArgs = 3;
177 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
178 pReq->u.Internal.aArgs[1] = (uintptr_t)buf;
179 pReq->u.Internal.aArgs[2] = (uintptr_t)cb;
180 pReq->fFlags = RTREQFLAGS_VOID|RTREQFLAGS_NO_WAIT;
181
182 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
183 AssertReleaseRC(rc);
184# ifndef RT_OS_WINDOWS
185 /* kick select() */
186 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
187 AssertRC(rc);
188# else
189 /* kick WSAWaitForMultipleEvents */
190 rc = WSASetEvent(pThis->hWakeupEvent);
191 AssertRelease(rc == TRUE);
192# endif
193
194#else /* !VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
195
196 int rc = RTCritSectEnter(&pThis->CritSect);
197 AssertReleaseRC(rc);
198
199 drvNATSendWorker(pThis, pvBuf, cb);
200
201 RTCritSectLeave(&pThis->CritSect);
202
203#endif /* !VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
204
205 LogFlow(("drvNATSend: end\n"));
206 return VINF_SUCCESS;
207}
208
209
210/**
211 * Set promiscuous mode.
212 *
213 * This is called when the promiscuous mode is set. This means that there doesn't have
214 * to be a mode change when it's called.
215 *
216 * @param pInterface Pointer to the interface structure containing the called function pointer.
217 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
218 * @thread EMT
219 */
220static DECLCALLBACK(void) drvNATSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
221{
222 LogFlow(("drvNATSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
223 /* nothing to do */
224}
225
226/**
227 * Worker function for drvNATNotifyLinkChanged().
228 * @thread "NAT" thread.
229 */
230static void drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
231{
232 pThis->enmLinkState = enmLinkState;
233
234 switch (enmLinkState)
235 {
236 case PDMNETWORKLINKSTATE_UP:
237 LogRel(("NAT: link up\n"));
238 slirp_link_up(pThis->pNATState);
239 break;
240
241 case PDMNETWORKLINKSTATE_DOWN:
242 case PDMNETWORKLINKSTATE_DOWN_RESUME:
243 LogRel(("NAT: link down\n"));
244 slirp_link_down(pThis->pNATState);
245 break;
246
247 default:
248 AssertMsgFailed(("drvNATNotifyLinkChanged: unexpected link state %d\n", enmLinkState));
249 }
250}
251
252/**
253 * Notification on link status changes.
254 *
255 * @param pInterface Pointer to the interface structure containing the called function pointer.
256 * @param enmLinkState The new link state.
257 * @thread EMT
258 */
259static DECLCALLBACK(void) drvNATNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
260{
261 PDRVNAT pThis = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
262
263 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
264
265#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
266
267 PRTREQ pReq = NULL;
268 /* don't queue new requests when the NAT thread is about to stop */
269 if (pThis->pThread->enmState != PDMTHREADSTATE_RUNNING)
270 return;
271 int rc = RTReqAlloc(pThis->pReqQueue, &pReq, RTREQTYPE_INTERNAL);
272 AssertReleaseRC(rc);
273 pReq->u.Internal.pfn = (PFNRT)drvNATNotifyLinkChangedWorker;
274 pReq->u.Internal.cArgs = 2;
275 pReq->u.Internal.aArgs[0] = (uintptr_t)pThis;
276 pReq->u.Internal.aArgs[1] = (uintptr_t)enmLinkState;
277 pReq->fFlags = RTREQFLAGS_VOID;
278 rc = RTReqQueue(pReq, 0); /* don't wait, we have to wakeup the NAT thread fist */
279 if (RT_LIKELY(rc == VERR_TIMEOUT))
280 {
281# ifndef RT_OS_WINDOWS
282 /* kick select() */
283 rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
284 AssertRC(rc);
285# else
286 /* kick WSAWaitForMultipleEvents() */
287 rc = WSASetEvent(pThis->hWakeupEvent);
288 AssertRelease(rc == TRUE);
289# endif
290 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
291 AssertReleaseRC(rc);
292 }
293 else
294 AssertReleaseRC(rc);
295 RTReqFree(pReq);
296
297#else /* !VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
298
299 int rc = RTCritSectEnter(&pThis->CritSect);
300 AssertReleaseRC(rc);
301 drvNATNotifyLinkChangedWorker(pThis, enmLinkState);
302 RTCritSectLeave(&pThis->CritSect);
303
304#endif /* VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
305}
306
307
308#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
309
310/**
311 * Poller callback.
312 */
313static DECLCALLBACK(void) drvNATPoller(PPDMDRVINS pDrvIns)
314{
315 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
316 fd_set ReadFDs;
317 fd_set WriteFDs;
318 fd_set XcptFDs;
319 int nFDs = -1;
320 FD_ZERO(&ReadFDs);
321 FD_ZERO(&WriteFDs);
322 FD_ZERO(&XcptFDs);
323
324 int rc = RTCritSectEnter(&pThis->CritSect);
325 AssertReleaseRC(rc);
326
327 slirp_select_fill(pThis->pNATState, &nFDs, &ReadFDs, &WriteFDs, &XcptFDs);
328
329 struct timeval tv = {0, 0}; /* no wait */
330 int cChangedFDs = select(nFDs + 1, &ReadFDs, &WriteFDs, &XcptFDs, &tv);
331 if (cChangedFDs >= 0)
332 slirp_select_poll(pThis->pNATState, &ReadFDs, &WriteFDs, &XcptFDs);
333
334 RTCritSectLeave(&pThis->CritSect);
335}
336
337#else /* VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
338
339static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
340{
341 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
342 fd_set ReadFDs;
343 fd_set WriteFDs;
344 fd_set XcptFDs;
345 int nFDs = -1;
346 unsigned int ms;
347# ifdef RT_OS_WINDOWS
348 DWORD event;
349 HANDLE *phEvents;
350 unsigned int cBreak = 0;
351# else
352 struct pollfd *polls;
353# endif
354
355 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
356
357 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
358 return VINF_SUCCESS;
359
360#ifdef RT_OS_WINDOWS
361 phEvents = slirp_get_events(pThis->pNATState);
362#endif
363
364 /*
365 * Polling loop.
366 */
367 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
368 {
369 nFDs = -1;
370
371 /*
372 * To prevent concurent execution of sending/receving threads
373 */
374# ifndef RT_OS_WINDOWS
375 nFDs = slirp_get_nsock(pThis->pNATState);
376 polls = (struct pollfd *)RTMemAllocZ((2 + nFDs) * sizeof(struct pollfd)); /* allocation for all sockets + ICMP and Management pipe*/
377 if (polls == NULL)
378 {
379 LogRel(("Can't allocate memory for polling\n"));
380 AssertRelease(polls);
381 }
382
383 slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]); /*don't bother Slirp with knowelege about managemant pipe*/
384 ms = slirp_get_timeout_ms(pThis->pNATState);
385
386 polls[0].fd = pThis->PipeRead;
387 polls[0].events = POLLRDNORM|POLLPRI|POLLRDBAND; /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
388 int cChangedFDs = poll(polls, nFDs + 2, ms ? ms : -1);
389 if (cChangedFDs >= 0)
390 {
391 slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
392 if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
393 {
394 /* drain the pipe */
395 char ch[1];
396 size_t cbRead;
397 RTFileRead(pThis->PipeRead, &ch, 1, &cbRead);
398 }
399 /* process _all_ outstanding requests but don't wait */
400 RTReqProcess(pThis->pReqQueue, 0);
401 }
402 RTMemFree(polls);
403# else /* RT_OS_WINDOWS */
404 slirp_select_fill(pThis->pNATState, &nFDs);
405 ms = slirp_get_timeout_ms(pThis->pNATState);
406 struct timeval tv = { 0, ms*1000 };
407 event = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE, ms ? ms : WSA_INFINITE, FALSE);
408 if ( (event < WSA_WAIT_EVENT_0 || event > WSA_WAIT_EVENT_0 + nFDs - 1)
409 && event != WSA_WAIT_TIMEOUT)
410 {
411 int error = WSAGetLastError();
412 LogRel(("WSAWaitForMultipleEvents returned %d (error %d)\n", event, error));
413 RTAssertReleasePanic();
414 }
415
416 if (event == WSA_WAIT_TIMEOUT)
417 {
418 /* only check for slow/fast timers */
419 slirp_select_poll(pThis->pNATState, /* fTimeout=*/true, /*fIcmp=*/false);
420 Log2(("%s: timeout\n", __FUNCTION__));
421 continue;
422 }
423
424 /* poll the sockets in any case */
425 Log2(("%s: poll\n", __FUNCTION__));
426 slirp_select_poll(pThis->pNATState, /* fTimeout=*/false, /* fIcmp=*/(event == WSA_WAIT_EVENT_0));
427 /* process _all_ outstanding requests but don't wait */
428 RTReqProcess(pThis->pReqQueue, 0);
429# ifdef VBOX_NAT_DELAY_HACK
430 if (cBreak++ > 128)
431 {
432 cBreak = 0;
433 RTThreadSleep(2);
434 }
435# endif
436# endif /* RT_OS_WINDOWS */
437 }
438
439 return VINF_SUCCESS;
440}
441
442 /**
443 * Unblock the send thread so it can respond to a state change.
444 *
445 * @returns VBox status code.
446 * @param pDevIns The pcnet device instance.
447 * @param pThread The send thread.
448 */
449static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
450{
451 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
452
453# ifndef RT_OS_WINDOWS
454 /* kick select() */
455 int rc = RTFileWrite(pThis->PipeWrite, "", 1, NULL);
456 AssertRC(rc);
457# else
458 /* kick WSAWaitForMultipleEvents() */
459 WSASetEvent(pThis->hWakeupEvent);
460# endif
461
462 return VINF_SUCCESS;
463}
464
465#ifdef VBOX_WITH_SLIRP_MT
466static DECLCALLBACK(int) drvNATAsyncIoGuest(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
467{
468 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
469 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
470 return VINF_SUCCESS;
471 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
472 {
473 slirp_process_queue(pThis->pNATState);
474 }
475 return VINF_SUCCESS;
476}
477
478static DECLCALLBACK(int) drvNATAsyncIoGuestWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
479{
480 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
481
482 return VINF_SUCCESS;
483}
484#endif
485
486#endif /* VBOX_WITH_SIMPLIFIED_SLIRP_SYNC */
487
488
489/**
490 * Function called by slirp to check if it's possible to feed incoming data to the network port.
491 * @returns 1 if possible.
492 * @returns 0 if not possible.
493 */
494int slirp_can_output(void *pvUser)
495{
496 PDRVNAT pThis = (PDRVNAT)pvUser;
497
498 Assert(pThis);
499
500#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
501 /** Happens during termination */
502 if (!RTCritSectIsOwner(&pThis->CritSect))
503 return 0;
504
505 int rc = pThis->pPort->pfnWaitReceiveAvail(pThis->pPort, 0);
506 return RT_SUCCESS(rc);
507#else
508 return 1;
509#endif
510}
511
512
513/**
514 * Function called by slirp to feed incoming data to the network port.
515 */
516#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
517void slirp_output(void *pvUser, void *pvArg, const uint8_t *pu8Buf, int cb)
518#else
519void slirp_output(void *pvUser, const uint8_t *pu8Buf, int cb)
520#endif
521{
522 PDRVNAT pThis = (PDRVNAT)pvUser;
523
524 LogFlow(("slirp_output BEGIN %x %d\n", pu8Buf, cb));
525 Log2(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
526
527 Assert(pThis);
528
529#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
530 /** Happens during termination */
531 if (!RTCritSectIsOwner(&pThis->CritSect))
532 return;
533
534 int rc = pThis->pPort->pfnReceive(pThis->pPort, pu8Buf, cb);
535 AssertRC(rc);
536 LogFlow(("slirp_output END %x %d\n", pu8Buf, cb));
537#else
538
539 PDRVNATQUEUITEM pItem = (PDRVNATQUEUITEM)PDMQueueAlloc(pThis->pSendQueue);
540 if (pItem)
541 {
542 pItem->pu8Buf = pu8Buf;
543 pItem->cb = cb;
544 pItem->mbuf = pvArg;
545 Log2(("pItem:%p %.Rhxd\n", pItem, pItem->pu8Buf));
546 PDMQueueInsert(pThis->pSendQueue, &pItem->Core);
547 return;
548 }
549 static unsigned cDroppedPackets;
550 if (cDroppedPackets < 64)
551 {
552 cDroppedPackets++;
553 LogRel(("NAT: Dropping package (couldn't alloc queue item to)\n"));
554 }
555 RTMemFree((void *)pu8Buf);
556#endif
557}
558
559#ifdef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
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#endif
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 * Destruct a driver instance.
625 *
626 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
627 * resources can be freed correctly.
628 *
629 * @param pDrvIns The driver instance data.
630 */
631static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
632{
633 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
634
635 LogFlow(("drvNATDestruct:\n"));
636
637#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
638 int rc = RTCritSectEnter(&pThis->CritSect);
639 AssertReleaseRC(rc);
640#endif
641 slirp_term(pThis->pNATState);
642 pThis->pNATState = NULL;
643#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
644 RTCritSectLeave(&pThis->CritSect);
645 RTCritSectDelete(&pThis->CritSect);
646#endif
647}
648
649
650/**
651 * Sets up the redirectors.
652 *
653 * @returns VBox status code.
654 * @param pCfgHandle The drivers configuration handle.
655 */
656static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfgHandle, RTIPV4ADDR Network)
657{
658 /*
659 * Enumerate redirections.
660 */
661 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfgHandle); pNode; pNode = CFGMR3GetNextChild(pNode))
662 {
663 /*
664 * Validate the port forwarding config.
665 */
666 if (!CFGMR3AreValuesValid(pNode, "Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0"))
667 return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown configuration in port forwarding"));
668
669 /* protocol type */
670 bool fUDP;
671 char szProtocol[32];
672 int rc = CFGMR3QueryString(pNode, "Protocol", &szProtocol[0], sizeof(szProtocol));
673 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
674 {
675 rc = CFGMR3QueryBool(pNode, "UDP", &fUDP);
676 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
677 fUDP = false;
678 else if (RT_FAILURE(rc))
679 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"UDP\" boolean failed"), iInstance);
680 }
681 else if (RT_SUCCESS(rc))
682 {
683 if (!RTStrICmp(szProtocol, "TCP"))
684 fUDP = false;
685 else if (!RTStrICmp(szProtocol, "UDP"))
686 fUDP = true;
687 else
688 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS, N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""), iInstance, szProtocol);
689 }
690 else
691 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"Protocol\" string failed"), iInstance);
692
693 /* host port */
694 int32_t iHostPort;
695 rc = CFGMR3QueryS32(pNode, "HostPort", &iHostPort);
696 if (RT_FAILURE(rc))
697 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"HostPort\" integer failed"), iInstance);
698
699 /* guest port */
700 int32_t iGuestPort;
701 rc = CFGMR3QueryS32(pNode, "GuestPort", &iGuestPort);
702 if (RT_FAILURE(rc))
703 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"GuestPort\" integer failed"), iInstance);
704
705 /* guest address */
706 char szGuestIP[32];
707 rc = CFGMR3QueryString(pNode, "GuestIP", &szGuestIP[0], sizeof(szGuestIP));
708 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
709 RTStrPrintf(szGuestIP, sizeof(szGuestIP), "%d.%d.%d.%d",
710 (Network & 0xFF000000) >> 24, (Network & 0xFF0000) >> 16, (Network & 0xFF00) >> 8, (Network & 0xE0) | 15);
711 else if (RT_FAILURE(rc))
712 return PDMDrvHlpVMSetError(pThis->pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"GuestIP\" string failed"), iInstance);
713 struct in_addr GuestIP;
714 if (!inet_aton(szGuestIP, &GuestIP))
715 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_GUEST_IP, RT_SRC_POS,
716 N_("NAT#%d: configuration error: invalid \"GuestIP\"=\"%s\", inet_aton failed"), iInstance, szGuestIP);
717
718 /*
719 * Call slirp about it.
720 */
721 Log(("drvNATConstruct: Redir %d -> %s:%d\n", iHostPort, szGuestIP, iGuestPort));
722 if (slirp_redir(pThis->pNATState, fUDP, iHostPort, GuestIP, iGuestPort) < 0)
723 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
724 N_("NAT#%d: configuration error: failed to set up redirection of %d to %s:%d. Probably a conflict with existing services or other rules"), iInstance, iHostPort, szGuestIP, iGuestPort);
725 } /* for each redir rule */
726
727 return VINF_SUCCESS;
728}
729
730/**
731 * Get the MAC address into the slirp stack.
732 */
733static void drvNATSetMac(PDRVNAT pThis)
734{
735 if (pThis->pConfig)
736 {
737 RTMAC Mac;
738 pThis->pConfig->pfnGetMac(pThis->pConfig, &Mac);
739 slirp_set_ethaddr(pThis->pNATState, Mac.au8);
740 }
741}
742
743
744/**
745 * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
746 * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
747 * (usually done during guest boot).
748 */
749static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSMHandle)
750{
751 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
752 drvNATSetMac(pThis);
753 return VINF_SUCCESS;
754}
755
756
757/**
758 * Some guests might not use DHCP to retrieve an IP but use a static IP.
759 */
760static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
761{
762 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
763 drvNATSetMac(pThis);
764}
765
766
767/**
768 * Construct a NAT network transport driver instance.
769 *
770 * @returns VBox status.
771 * @param pDrvIns The driver instance data.
772 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
773 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
774 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
775 * iInstance it's expected to be used a bit in this function.
776 */
777static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
778{
779 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
780 char szNetAddr[16];
781 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
782 LogFlow(("drvNATConstruct:\n"));
783
784 /*
785 * Validate the config.
786 */
787 if (!CFGMR3AreValuesValid(pCfgHandle, "PassDomain\0TFTPPrefix\0BootFile\0Network\0"))
788 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, N_("Unknown NAT configuration option, only supports PassDomain, TFTPPrefix, BootFile and Network"));
789
790 /*
791 * Init the static parts.
792 */
793 pThis->pDrvIns = pDrvIns;
794 pThis->pNATState = NULL;
795 pThis->pszTFTPPrefix = NULL;
796 pThis->pszBootFile = NULL;
797 /* IBase */
798 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
799 /* INetwork */
800 pThis->INetworkConnector.pfnSend = drvNATSend;
801 pThis->INetworkConnector.pfnSetPromiscuousMode = drvNATSetPromiscuousMode;
802 pThis->INetworkConnector.pfnNotifyLinkChanged = drvNATNotifyLinkChanged;
803
804 /*
805 * Get the configuration settings.
806 */
807 bool fPassDomain = true;
808 int rc = CFGMR3QueryBool(pCfgHandle, "PassDomain", &fPassDomain);
809 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
810 fPassDomain = true;
811 else if (RT_FAILURE(rc))
812 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"PassDomain\" boolean failed"), pDrvIns->iInstance);
813
814 rc = CFGMR3QueryStringAlloc(pCfgHandle, "TFTPPrefix", &pThis->pszTFTPPrefix);
815 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
816 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"TFTPPrefix\" string failed"), pDrvIns->iInstance);
817 rc = CFGMR3QueryStringAlloc(pCfgHandle, "BootFile", &pThis->pszBootFile);
818 if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
819 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"BootFile\" string failed"), pDrvIns->iInstance);
820
821 /*
822 * Query the network port interface.
823 */
824 pThis->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
825 if (!pThis->pPort)
826 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
827 N_("Configuration error: the above device/driver didn't export the network port interface"));
828 pThis->pConfig = (PPDMINETWORKCONFIG)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_CONFIG);
829 if (!pThis->pConfig)
830 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
831 N_("Configuration error: the above device/driver didn't export the network config interface"));
832
833 /* Generate a network address for this network card. */
834 rc = CFGMR3QueryString(pCfgHandle, "Network", szNetwork, sizeof(szNetwork));
835 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
836 RTStrPrintf(szNetwork, sizeof(szNetwork), "10.0.%d.0/24", pDrvIns->iInstance + 2);
837 else if (RT_FAILURE(rc))
838 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: configuration query for \"Network\" string failed"), pDrvIns->iInstance);
839
840 RTIPV4ADDR Network;
841 RTIPV4ADDR Netmask;
842 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
843 if (RT_FAILURE(rc))
844 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT#%d: Configuration error: network '%s' describes not a valid IPv4 network"), pDrvIns->iInstance, szNetwork);
845
846 RTStrPrintf(szNetAddr, sizeof(szNetAddr), "%d.%d.%d.%d",
847 (Network & 0xFF000000) >> 24, (Network & 0xFF0000) >> 16, (Network & 0xFF00) >> 8, Network & 0xFF);
848
849 /*
850 * The slirp lock..
851 */
852#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
853 rc = RTCritSectInit(&pThis->CritSect);
854 if (RT_FAILURE(rc))
855 return rc;
856#endif
857 /*
858 * Initialize slirp.
859 */
860 rc = slirp_init(&pThis->pNATState, &szNetAddr[0], Netmask, fPassDomain, pThis->pszTFTPPrefix, pThis->pszBootFile, pThis);
861 if (RT_SUCCESS(rc))
862 {
863 slirp_register_timers(pThis->pNATState, pDrvIns);
864 int rc2 = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfgHandle, Network);
865 if (RT_SUCCESS(rc2))
866 {
867 /*
868 * Register a load done notification to get the MAC address into the slirp
869 * engine after we loaded a guest state.
870 */
871 rc2 = PDMDrvHlpSSMRegister(pDrvIns, pDrvIns->pDrvReg->szDriverName,
872 pDrvIns->iInstance, 0, 0,
873 NULL, NULL, NULL, NULL, NULL, drvNATLoadDone);
874 AssertRC(rc2);
875#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
876 pDrvIns->pDrvHlp->pfnPDMPollerRegister(pDrvIns, drvNATPoller);
877#else
878 rc = RTReqCreateQueue(&pThis->pReqQueue);
879 if (RT_FAILURE(rc))
880 {
881 LogRel(("Can't create request queue\n"));
882 return rc;
883 }
884
885 rc = PDMDrvHlpPDMQueueCreate(pDrvIns, sizeof(DRVNATQUEUITEM), 50, 0, drvNATQueueConsumer, &pThis->pSendQueue);
886 if (RT_FAILURE(rc))
887 {
888 LogRel(("Can't create send queue\n"));
889 return rc;
890 }
891
892# ifndef RT_OS_WINDOWS
893 /*
894 * Create the control pipe.
895 */
896 int fds[2];
897 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
898 {
899 int rc = RTErrConvertFromErrno(errno);
900 AssertRC(rc);
901 return rc;
902 }
903 pThis->PipeRead = fds[0];
904 pThis->PipeWrite = fds[1];
905# else
906 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
907 slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent, VBOX_WAKEUP_EVENT_INDEX);
908# endif
909
910 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pThread, pThis, drvNATAsyncIoThread, drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
911 AssertReleaseRC(rc);
912
913#ifdef VBOX_WITH_SLIRP_MT
914 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pGuestThread, pThis, drvNATAsyncIoGuest, drvNATAsyncIoGuestWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATGUEST");
915 AssertReleaseRC(rc);
916#endif
917#endif
918
919 pThis->enmLinkState = PDMNETWORKLINKSTATE_UP;
920
921 /* might return VINF_NAT_DNS */
922 return rc;
923 }
924 /* failure path */
925 rc = rc2;
926 slirp_term(pThis->pNATState);
927 pThis->pNATState = NULL;
928 }
929 else
930 {
931 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
932 AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
933 }
934
935#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
936 RTCritSectDelete(&pThis->CritSect);
937#endif
938 return rc;
939}
940
941
942/**
943 * NAT network transport driver registration record.
944 */
945const PDMDRVREG g_DrvNAT =
946{
947 /* u32Version */
948 PDM_DRVREG_VERSION,
949 /* szDriverName */
950 "NAT",
951 /* pszDescription */
952 "NAT Network Transport Driver",
953 /* fFlags */
954 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
955 /* fClass. */
956 PDM_DRVREG_CLASS_NETWORK,
957 /* cMaxInstances */
958 16,
959 /* cbInstance */
960 sizeof(DRVNAT),
961 /* pfnConstruct */
962 drvNATConstruct,
963 /* pfnDestruct */
964 drvNATDestruct,
965 /* pfnIOCtl */
966 NULL,
967 /* pfnPowerOn */
968 drvNATPowerOn,
969 /* pfnReset */
970 NULL,
971 /* pfnSuspend */
972 NULL,
973 /* pfnResume */
974 NULL,
975 /* pfnDetach */
976 NULL,
977 /* pfnPowerOff */
978 NULL
979};
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