VirtualBox

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

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

NAT: 1. wo sync enhancement branch is still functional (was corrupted with using ICMP file handler in select(1))

  1. after sending send queue doesn't need to synchronize with NAT thread to free mbuf instead NAT queue used to call freeing slirp routine.
  2. no more copying on slirp to guest sent.


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