VirtualBox

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

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

Check link states when sending data.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette