VirtualBox

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

Last change on this file since 1033 was 1033, checked in by vboxsync, 18 years ago

Big change to make slirp fully instantiatable (replace all global
variables with local ones, passing a reference to the state/config
structure to all places which are interested). You can now have as many
cards in the guest configured for NAT networking as you want.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/** @file
2 *
3 * VBox network devices:
4 * NAT network transport driver
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_NAT
28#include "Network/slirp/libslirp.h"
29#include <VBox/pdm.h>
30#include <VBox/cfgm.h>
31#include <VBox/mm.h>
32#include <VBox/err.h>
33
34#include <VBox/log.h>
35#include <iprt/assert.h>
36#include <iprt/file.h>
37#include <iprt/string.h>
38#include <iprt/critsect.h>
39
40#include "Builtins.h"
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46/**
47 * Block driver instance data.
48 */
49typedef struct DRVNAT
50{
51 /** The network interface. */
52 PDMINETWORKCONNECTOR INetworkConnector;
53 /** The port we're attached to. */
54 PPDMINETWORKPORT pPort;
55 /** Pointer to the driver instance. */
56 PPDMDRVINS pDrvIns;
57 /** Slirp critical section. */
58 RTCRITSECT CritSect;
59 /** Link state */
60 PDMNETWORKLINKSTATE enmLinkState;
61 /** NAT state for this instance. */
62 PNATState pNATState;
63} DRVNAT, *PDRVNAT;
64
65/** Converts a pointer to NAT::INetworkConnector to a PRDVNAT. */
66#define PDMINETWORKCONNECTOR_2_DRVNAT(pInterface) ( (PDRVNAT)((uintptr_t)pInterface - RT_OFFSETOF(DRVNAT, INetworkConnector)) )
67
68/*******************************************************************************
69* Global Variables *
70*******************************************************************************/
71#if 0
72/** If set the thread should terminate. */
73static bool g_fThreadTerm = false;
74/** The thread id of the select thread (drvNATSelectThread()). */
75static RTTHREAD g_ThreadSelect;
76#endif
77
78
79/**
80 * Send data to the network.
81 *
82 * @returns VBox status code.
83 * @param pInterface Pointer to the interface structure containing the called function pointer.
84 * @param pvBuf Data to send.
85 * @param cb Number of bytes to send.
86 * @thread EMT
87 */
88static DECLCALLBACK(int) drvNATSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
89{
90 PDRVNAT pData = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
91
92 LogFlow(("drvNATSend: pvBuf=%p cb=%#x\n", pvBuf, cb));
93 Log2(("drvNATSend: pvBuf=%p cb=%#x\n"
94 "%.*Vhxd\n",
95 pvBuf, cb, cb, pvBuf));
96
97 int rc = RTCritSectEnter(&pData->CritSect);
98 AssertReleaseRC(rc);
99
100 Assert(pData->enmLinkState == PDMNETWORKLINKSTATE_UP);
101 if (pData->enmLinkState == PDMNETWORKLINKSTATE_UP)
102 slirp_input(pData->pNATState, (uint8_t *)pvBuf, cb);
103 RTCritSectLeave(&pData->CritSect);
104 LogFlow(("drvNATSend: end\n"));
105 return VINF_SUCCESS;
106}
107
108
109/**
110 * Set promiscuous mode.
111 *
112 * This is called when the promiscuous mode is set. This means that there doesn't have
113 * to be a mode change when it's called.
114 *
115 * @param pInterface Pointer to the interface structure containing the called function pointer.
116 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
117 * @thread EMT
118 */
119static DECLCALLBACK(void) drvNATSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
120{
121 LogFlow(("drvNATSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
122 /* nothing to do */
123}
124
125
126/**
127 * Notification on link status changes.
128 *
129 * @param pInterface Pointer to the interface structure containing the called function pointer.
130 * @param enmLinkState The new link state.
131 * @thread EMT
132 */
133static DECLCALLBACK(void) drvNATNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
134{
135 PDRVNAT pData = PDMINETWORKCONNECTOR_2_DRVNAT(pInterface);
136
137 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
138
139 int rc = RTCritSectEnter(&pData->CritSect);
140 AssertReleaseRC(rc);
141 pData->enmLinkState = enmLinkState;
142
143 switch (enmLinkState)
144 {
145 case PDMNETWORKLINKSTATE_UP:
146 LogRel(("NAT: link up\n"));
147 slirp_link_up(pData->pNATState);
148 break;
149
150 case PDMNETWORKLINKSTATE_DOWN:
151 case PDMNETWORKLINKSTATE_DOWN_RESUME:
152 LogRel(("NAT: link down\n"));
153 slirp_link_down(pData->pNATState);
154 break;
155
156 default:
157 AssertMsgFailed(("drvNATNotifyLinkChanged: unexpected link state %d\n", enmLinkState));
158 }
159 RTCritSectLeave(&pData->CritSect);
160}
161
162
163/**
164 * More receive buffer has become available.
165 *
166 * This is called when the NIC frees up receive buffers.
167 *
168 * @param pInterface Pointer to the interface structure containing the called function pointer.
169 * @thread EMT
170 */
171static DECLCALLBACK(void) drvNATNotifyCanReceive(PPDMINETWORKCONNECTOR pInterface)
172{
173 LogFlow(("drvNATNotifyCanReceive:\n"));
174 /** @todo do something useful here. */
175}
176
177
178/**
179 * Poller callback.
180 */
181static DECLCALLBACK(void) drvNATPoller(PPDMDRVINS pDrvIns)
182{
183 PDRVNAT pData = PDMINS2DATA(pDrvIns, PDRVNAT);
184 fd_set ReadFDs;
185 fd_set WriteFDs;
186 fd_set XcptFDs;
187 int cFDs = -1;
188 FD_ZERO(&ReadFDs);
189 FD_ZERO(&WriteFDs);
190 FD_ZERO(&XcptFDs);
191
192 int rc = RTCritSectEnter(&pData->CritSect);
193 AssertReleaseRC(rc);
194
195 slirp_select_fill(pData->pNATState, &cFDs, &ReadFDs, &WriteFDs, &XcptFDs);
196
197 struct timeval tv = {0, 0}; /* no wait */
198 int cReadFDs = select(cFDs + 1, &ReadFDs, &WriteFDs, &XcptFDs, &tv);
199 if (cReadFDs >= 0)
200 slirp_select_poll(pData->pNATState, &ReadFDs, &WriteFDs, &XcptFDs);
201
202 RTCritSectLeave(&pData->CritSect);
203}
204
205
206/**
207 * Function called by slirp to check if it's possible to feed incoming data to the network port.
208 * @returns 1 if possible.
209 * @returns 0 if not possible.
210 */
211int slirp_can_output(void *pvUser)
212{
213 PDRVNAT pData = (PDRVNAT)pvUser;
214
215 Assert(pData);
216
217 /** Happens during termination */
218 if (!RTCritSectIsOwner(&pData->CritSect))
219 return 0;
220
221 return pData->pPort->pfnCanReceive(pData->pPort);
222
223 return 0;
224}
225
226
227/**
228 * Function called by slirp to feed incoming data to the network port.
229 */
230void slirp_output(void *pvUser, const uint8_t *pu8Buf, int cb)
231{
232 PDRVNAT pData = (PDRVNAT)pvUser;
233
234 LogFlow(("slirp_output BEGING %x %d\n", pu8Buf, cb));
235 Log2(("slirp_output: pu8Buf=%p cb=%#x (pData=%p)\n"
236 "%.*Vhxd\n",
237 pu8Buf, cb, pData,
238 cb, pu8Buf));
239
240 Assert(pData);
241
242 /** Happens during termination */
243 if (!RTCritSectIsOwner(&pData->CritSect))
244 return;
245
246 int rc = pData->pPort->pfnReceive(pData->pPort, pu8Buf, cb);
247 AssertRC(rc);
248 LogFlow(("slirp_output END %x %d\n", pu8Buf, cb));
249}
250
251/**
252 * Queries an interface to the driver.
253 *
254 * @returns Pointer to interface.
255 * @returns NULL if the interface was not supported by the driver.
256 * @param pInterface Pointer to this interface structure.
257 * @param enmInterface The requested interface identification.
258 * @thread Any thread.
259 */
260static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
261{
262 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
263 PDRVNAT pData = PDMINS2DATA(pDrvIns, PDRVNAT);
264 switch (enmInterface)
265 {
266 case PDMINTERFACE_BASE:
267 return &pDrvIns->IBase;
268 case PDMINTERFACE_NETWORK_CONNECTOR:
269 return &pData->INetworkConnector;
270 default:
271 return NULL;
272 }
273}
274
275
276/**
277 * Destruct a driver instance.
278 *
279 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
280 * resources can be freed correctly.
281 *
282 * @param pDrvIns The driver instance data.
283 */
284static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
285{
286 PDRVNAT pData = PDMINS2DATA(pDrvIns, PDRVNAT);
287
288 LogFlow(("drvNATDestruct:\n"));
289
290#if ARCH_BITS == 64
291 LogRel(("NAT: g_cpvHashUsed=%RU32 g_cpvHashCollisions=%RU32 g_cpvHashInserts=%RU64 g_cpvHashDone=%RU64\n",
292 g_cpvHashUsed, g_cpvHashCollisions, g_cpvHashInserts, g_cpvHashDone));
293#endif
294 int rc = RTCritSectEnter(&pData->CritSect);
295 AssertReleaseRC(rc);
296 slirp_term(pData->pNATState);
297 pData->pNATState = NULL;
298 RTCritSectLeave(&pData->CritSect);
299
300 RTCritSectDelete(&pData->CritSect);
301}
302
303
304/**
305 * Sets up the redirectors.
306 *
307 * @returns VBox status code.
308 * @param pCfgHandle The drivers configuration handle.
309 */
310static int drvNATConstructRedir(PDRVNAT pData, PCFGMNODE pCfgHandle)
311{
312 /*
313 * Enumerate redirections.
314 */
315 for (PCFGMNODE pNode = CFGMR3GetFirstChild(pCfgHandle); pNode; pNode = CFGMR3GetNextChild(pNode))
316 {
317 /* protocol type */
318 bool fUDP;
319 int rc = CFGMR3QueryBool(pNode, "UDP", &fUDP);
320 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
321 fUDP = true;
322 else if (VBOX_FAILURE(rc))
323 {
324 AssertMsgFailed(("Configuration error: Boolean \"UDP\" -> %Vrc\n", rc));
325 return rc;
326 }
327
328 /* host port */
329 int32_t iHostPort;
330 rc = CFGMR3QueryS32(pNode, "HostPort", &iHostPort);
331 if (VBOX_FAILURE(rc))
332 {
333 AssertMsgFailed(("Configuration error: Boolean \"HostPort\" -> %Vrc\n", rc));
334 return rc;
335 }
336
337 /* guest port */
338 int32_t iGuestPort;
339 rc = CFGMR3QueryS32(pNode, "GuestPort", &iGuestPort);
340 if (VBOX_FAILURE(rc))
341 {
342 AssertMsgFailed(("Configuration error: Boolean \"GuestPort\" -> %Vrc\n", rc));
343 return rc;
344 }
345
346 /* guest address */
347 char szGuestIP[32];
348 rc = CFGMR3QueryString(pNode, "GuestIP", &szGuestIP[0], sizeof(szGuestIP));
349 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
350 strcpy(szGuestIP, "10.0.2.15");
351 else if (VBOX_FAILURE(rc))
352 {
353 AssertMsgFailed(("Configuration error: Boolean \"HostPort\" -> %Vrc\n", rc));
354 return rc;
355 }
356 struct in_addr GuestIP;
357 if (!inet_aton(szGuestIP, &GuestIP))
358 {
359 AssertMsgFailed(("Configuration error: Invalid \"GuestIP\"=\"%s\", inet_aton failed.\n", szGuestIP));
360 return VERR_NAT_REDIR_GUEST_IP;
361 }
362
363 /*
364 * Call slirp about it.
365 */
366 Log(("drvNATConstruct: Redir %d -> %s:%d\n", iHostPort, szGuestIP, iGuestPort));
367 if (slirp_redir(pData->pNATState, fUDP, iHostPort, GuestIP, iGuestPort) < 0)
368 {
369 AssertMsgFailed(("Configuration error: failed to setup redirection of %d to %s:%d. Probably a conflict with existing services or other rules.\n",
370 iHostPort, szGuestIP, iGuestPort));
371 return VERR_NAT_REDIR_SETUP;
372 }
373 } /* for each redir rule */
374
375 return VINF_SUCCESS;
376}
377
378
379/**
380 * Construct a NAT network transport driver instance.
381 *
382 * @returns VBox status.
383 * @param pDrvIns The driver instance data.
384 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
385 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
386 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
387 * iInstance it's expected to be used a bit in this function.
388 */
389static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
390{
391 PDRVNAT pData = PDMINS2DATA(pDrvIns, PDRVNAT);
392 char szNetAddr[16];
393 LogFlow(("drvNATConstruct:\n"));
394
395 /*
396 * Validate the config.
397 */
398 if (!CFGMR3AreValuesValid(pCfgHandle, "\0"))
399 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
400
401 /*
402 * Init the static parts.
403 */
404 pData->pDrvIns = pDrvIns;
405 /* IBase */
406 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
407 /* INetwork */
408 pData->INetworkConnector.pfnSend = drvNATSend;
409 pData->INetworkConnector.pfnSetPromiscuousMode = drvNATSetPromiscuousMode;
410 pData->INetworkConnector.pfnNotifyLinkChanged = drvNATNotifyLinkChanged;
411 pData->INetworkConnector.pfnNotifyCanReceive = drvNATNotifyCanReceive;
412
413 pData->pNATState = NULL;
414
415 /*
416 * Query the network port interface.
417 */
418 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
419 if (!pData->pPort)
420 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
421 N_("Configuration error: the above device/driver didn't export the network port interface!\n"));
422
423 /* Generate a network address for this network card. */
424 RTStrPrintf(szNetAddr, sizeof(szNetAddr), "10.0.%d.0", pDrvIns->iInstance + 2);
425
426 /*
427 * The slirp lock..
428 */
429 int rc = RTCritSectInit(&pData->CritSect);
430 if (VBOX_FAILURE(rc))
431 return rc;
432#if 0
433 rc = RTSemEventCreate(&g_EventSem);
434 if (VBOX_SUCCESS(rc))
435 {
436 /*
437 * Start the select thread. (it'll block on the sem)
438 */
439 g_fThreadTerm = false;
440 rc = RTThreadCreate(&g_ThreadSelect, drvNATSelectThread, 0, NULL, "NATSEL");
441 if (VBOX_SUCCESS(rc))
442 {
443#endif
444 /*
445 * Initialize slirp.
446 */
447 rc = slirp_init(&pData->pNATState, &szNetAddr[0], pData);
448 if (VBOX_SUCCESS(rc))
449 {
450 rc = drvNATConstructRedir(pData, pCfgHandle);
451 if (VBOX_SUCCESS(rc))
452 {
453 pDrvIns->pDrvHlp->pfnPDMPollerRegister(pDrvIns, drvNATPoller);
454
455 pData->enmLinkState = PDMNETWORKLINKSTATE_UP;
456#if 0
457 RTSemEventSignal(g_EventSem);
458 RTThreadSleep(0);
459#endif
460 return VINF_SUCCESS;
461 }
462 /* failure path */
463 slirp_term(pData->pNATState);
464 pData->pNATState = NULL;
465 }
466 else
467 {
468 switch (rc)
469 {
470 case VERR_NAT_DNS:
471 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Domain Name Server (DNS) for NAT networking could not be determined"));
472 break;
473 default:
474 PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
475 AssertMsgFailed(("Add error message for rc=%d (%Vrc)\n", rc, rc));
476 break;
477 }
478 }
479#if 0
480 g_fThreadTerm = true;
481 RTSemEventSignal(g_EventSem);
482 RTThreadSleep(0);
483 }
484 RTSemEventDestroy(g_EventSem);
485 g_EventSem = NULL;
486 }
487#endif
488 RTCritSectDelete(&pData->CritSect);
489 return rc;
490}
491
492
493
494/**
495 * NAT network transport driver registration record.
496 */
497const PDMDRVREG g_DrvNAT =
498{
499 /* u32Version */
500 PDM_DRVREG_VERSION,
501 /* szDriverName */
502 "NAT",
503 /* pszDescription */
504 "NAT Network Transport Driver",
505 /* fFlags */
506 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
507 /* fClass. */
508 PDM_DRVREG_CLASS_NETWORK,
509 /* cMaxInstances */
510 16,
511 /* cbInstance */
512 sizeof(DRVNAT),
513 /* pfnConstruct */
514 drvNATConstruct,
515 /* pfnDestruct */
516 drvNATDestruct,
517 /* pfnIOCtl */
518 NULL,
519 /* pfnPowerOn */
520 NULL,
521 /* pfnReset */
522 NULL,
523 /* pfnSuspend */
524 NULL,
525 /* pfnResume */
526 NULL,
527 /* pfnDetach */
528 NULL,
529 /* pfnPowerOff */
530 NULL
531};
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