VirtualBox

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

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

prevent typecasts

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