VirtualBox

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

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

NAT: remove (and enable) VBOX_WITH_MULTI_DNS and VBOX_WITH_SLIRP_DNS_PROXY ifdefs as both were properly tested with VBox 2.2.x

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