VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvTAP.cpp@ 5320

Last change on this file since 5320 was 5298, checked in by vboxsync, 17 years ago

Fix for Solaris TAP deadlock. Try receiving when there's at least one free
buffer (Solaris pcn driver uses lots of 128-byte buffers).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.6 KB
Line 
1/** $Id: DrvTAP.cpp 5298 2007-10-15 17:22:37Z vboxsync $ */
2/** @file
3 * Universial TAP network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define ASYNC_NET
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_DRV_TUN
24#include <VBox/log.h>
25#include <VBox/pdmdrv.h>
26
27#include <iprt/assert.h>
28#include <iprt/file.h>
29#include <iprt/string.h>
30#include <iprt/path.h>
31#ifdef ASYNC_NET
32# include <iprt/thread.h>
33# include <iprt/asm.h>
34# include <iprt/semaphore.h>
35#endif
36
37#include <sys/ioctl.h>
38#include <sys/poll.h>
39#ifdef RT_OS_SOLARIS
40# include <sys/stat.h>
41# include <sys/ethernet.h>
42# include <sys/sockio.h>
43# include <netinet/in.h>
44# include <netinet/in_systm.h>
45# include <netinet/ip.h>
46# include <netinet/ip_icmp.h>
47# include <netinet/udp.h>
48# include <netinet/tcp.h>
49# include <net/if.h>
50# include <stropts.h>
51# include <fcntl.h>
52# include <ctype.h>
53# include <stdlib.h>
54#else
55# include <sys/fcntl.h>
56#endif
57#include <errno.h>
58#ifdef ASYNC_NET
59# include <unistd.h>
60#endif
61
62#ifdef RT_OS_L4
63# include <l4/vboxserver/file.h>
64#endif
65
66#include "Builtins.h"
67
68
69/*******************************************************************************
70* Structures and Typedefs *
71*******************************************************************************/
72typedef enum ASYNCSTATE
73{
74 //ASYNCSTATE_SUSPENDED = 1,
75 ASYNCSTATE_RUNNING,
76 ASYNCSTATE_TERMINATE
77} ASYNCSTATE;
78
79/**
80 * Block driver instance data.
81 */
82typedef struct DRVTAP
83{
84 /** The network interface. */
85 PDMINETWORKCONNECTOR INetworkConnector;
86 /** The network interface. */
87 PPDMINETWORKPORT pPort;
88 /** Pointer to the driver instance. */
89 PPDMDRVINS pDrvIns;
90 /** TAP device file handle. */
91 RTFILE FileDevice;
92 /** The configured TAP device name. */
93 char *pszDeviceName;
94#ifdef RT_OS_SOLARIS
95 /** The actual TAP device name. */
96 char *pszDeviceNameActual;
97 /** IP device file handle (/dev/udp). */
98 RTFILE IPFileDevice;
99#endif
100 /** TAP setup application. */
101 char *pszSetupApplication;
102 /** TAP terminate application. */
103 char *pszTerminateApplication;
104#ifdef ASYNC_NET
105 /** The write end of the control pipe. */
106 RTFILE PipeWrite;
107 /** The read end of the control pipe. */
108 RTFILE PipeRead;
109 /** The thread state. */
110 ASYNCSTATE volatile enmState;
111 /** Reader thread. */
112 RTTHREAD Thread;
113 /** We are waiting for more receive buffers. */
114 uint32_t volatile fOutOfSpace;
115 /** Event semaphore for blocking on receive. */
116 RTSEMEVENT EventOutOfSpace;
117#endif
118
119#ifdef VBOX_WITH_STATISTICS
120 /** Number of sent packets. */
121 STAMCOUNTER StatPktSent;
122 /** Number of sent bytes. */
123 STAMCOUNTER StatPktSentBytes;
124 /** Number of received packets. */
125 STAMCOUNTER StatPktRecv;
126 /** Number of received bytes. */
127 STAMCOUNTER StatPktRecvBytes;
128 /** Profiling packet transmit runs. */
129 STAMPROFILE StatTransmit;
130 /** Profiling packet receive runs. */
131 STAMPROFILEADV StatReceive;
132#ifdef ASYNC_NET
133 STAMPROFILE StatRecvOverflows;
134#endif
135#endif /* VBOX_WITH_STATISTICS */
136
137#ifdef LOG_ENABLED
138 /** The nano ts of the last transfer. */
139 uint64_t u64LastTransferTS;
140 /** The nano ts of the last receive. */
141 uint64_t u64LastReceiveTS;
142#endif
143} DRVTAP, *PDRVTAP;
144
145
146/** Converts a pointer to TAP::INetworkConnector to a PRDVTAP. */
147#define PDMINETWORKCONNECTOR_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkConnector)) )
148
149
150/*******************************************************************************
151* Internal Functions *
152*******************************************************************************/
153#ifdef RT_OS_SOLARIS
154static DECLCALLBACK(int) SolarisTAPAttach(PPDMDRVINS pDrvIns);
155#endif
156
157
158/**
159 * Send data to the network.
160 *
161 * @returns VBox status code.
162 * @param pInterface Pointer to the interface structure containing the called function pointer.
163 * @param pvBuf Data to send.
164 * @param cb Number of bytes to send.
165 * @thread EMT
166 */
167static DECLCALLBACK(int) drvTAPSend(PPDMINETWORKCONNECTOR pInterface, const void *pvBuf, size_t cb)
168{
169 PDRVTAP pData = PDMINETWORKCONNECTOR_2_DRVTAP(pInterface);
170 STAM_COUNTER_INC(&pData->StatPktSent);
171 STAM_COUNTER_ADD(&pData->StatPktSentBytes, cb);
172 STAM_PROFILE_START(&pData->StatTransmit, a);
173
174#ifdef LOG_ENABLED
175 uint64_t u64Now = RTTimeProgramNanoTS();
176 LogFlow(("drvTAPSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
177 cb, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
178 pData->u64LastTransferTS = u64Now;
179#endif
180 Log2(("drvTAPSend: pvBuf=%p cb=%#x\n"
181 "%.*Vhxd\n",
182 pvBuf, cb, cb, pvBuf));
183
184 int rc = RTFileWrite(pData->FileDevice, pvBuf, cb, NULL);
185
186 STAM_PROFILE_STOP(&pData->StatTransmit, a);
187 AssertRC(rc);
188 return rc;
189}
190
191
192/**
193 * Set promiscuous mode.
194 *
195 * This is called when the promiscuous mode is set. This means that there doesn't have
196 * to be a mode change when it's called.
197 *
198 * @param pInterface Pointer to the interface structure containing the called function pointer.
199 * @param fPromiscuous Set if the adaptor is now in promiscuous mode. Clear if it is not.
200 * @thread EMT
201 */
202static DECLCALLBACK(void) drvTAPSetPromiscuousMode(PPDMINETWORKCONNECTOR pInterface, bool fPromiscuous)
203{
204 LogFlow(("drvTAPSetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
205 /* nothing to do */
206}
207
208
209/**
210 * Notification on link status changes.
211 *
212 * @param pInterface Pointer to the interface structure containing the called function pointer.
213 * @param enmLinkState The new link state.
214 * @thread EMT
215 */
216static DECLCALLBACK(void) drvTAPNotifyLinkChanged(PPDMINETWORKCONNECTOR pInterface, PDMNETWORKLINKSTATE enmLinkState)
217{
218 LogFlow(("drvNATNotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
219 /** @todo take action on link down and up. Stop the polling and such like. */
220}
221
222
223/**
224 * More receive buffer has become available.
225 *
226 * This is called when the NIC frees up receive buffers.
227 *
228 * @param pInterface Pointer to the interface structure containing the called function pointer.
229 * @thread EMT
230 */
231static DECLCALLBACK(void) drvTAPNotifyCanReceive(PPDMINETWORKCONNECTOR pInterface)
232{
233 PDRVTAP pData = PDMINETWORKCONNECTOR_2_DRVTAP(pInterface);
234
235 LogFlow(("drvTAPNotifyCanReceive:\n"));
236 /** @todo r=bird: With a bit unfavorable scheduling it's possible to get here
237 * before fOutOfSpace is set by the overflow code. This will mean that, unless
238 * more receive descriptors become available, the receive thread will be stuck
239 * until it times out and cause a hickup in the network traffic.
240 * There is a simple, but not perfect, workaround for this problem in DrvTAPOs2.cpp.
241 *
242 * A better solution would be to ditch the NotifyCanReceive callback and instead
243 * change the CanReceive to do all the work. This will reduce the amount of code
244 * duplication, and would permit pcnet to avoid queuing unnecessary ring-3 tasks.
245 */
246
247 /* ensure we wake up only once */
248 if (ASMAtomicXchgU32(&pData->fOutOfSpace, false))
249 RTSemEventSignal(pData->EventOutOfSpace);
250}
251
252
253#ifdef ASYNC_NET
254/**
255 * Asynchronous I/O thread for handling receive.
256 *
257 * @returns VINF_SUCCESS (ignored).
258 * @param Thread Thread handle.
259 * @param pvUser Pointer to a DRVTAP structure.
260 */
261static DECLCALLBACK(int) drvTAPAsyncIoThread(RTTHREAD ThreadSelf, void *pvUser)
262{
263 PDRVTAP pData = (PDRVTAP)pvUser;
264 LogFlow(("drvTAPAsyncIoThread: pData=%p\n", pData));
265 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
266
267 int rc = RTSemEventCreate(&pData->EventOutOfSpace);
268 AssertRC(rc);
269
270 /*
271 * Polling loop.
272 */
273 for (;;)
274 {
275 /*
276 * Wait for something to become available.
277 */
278 struct pollfd aFDs[2];
279 aFDs[0].fd = pData->FileDevice;
280 aFDs[0].events = POLLIN | POLLPRI;
281 aFDs[0].revents = 0;
282 aFDs[1].fd = pData->PipeRead;
283 aFDs[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
284 aFDs[1].revents = 0;
285 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
286 errno=0;
287 rc = poll(&aFDs[0], ELEMENTS(aFDs), -1 /* infinite */);
288 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
289 if ( rc > 0
290 && (aFDs[0].revents & (POLLIN | POLLPRI))
291 && !aFDs[1].revents)
292 {
293 /*
294 * Read the frame.
295 */
296 char achBuf[4096];
297 size_t cbRead = 0;
298 rc = RTFileRead(pData->FileDevice, achBuf, sizeof(achBuf), &cbRead);
299 if (VBOX_SUCCESS(rc))
300 {
301 AssertMsg(cbRead <= 1536, ("cbRead=%d\n", cbRead));
302
303 /*
304 * Wait for the device to have space for this frame.
305 * Most guests use frame-sized receive buffers, hence non-zero cbMax
306 * automatically means there is enough room for entire frame. Some
307 * guests (eg. Solaris) use large chains of small receive buffers
308 * (each 128 or so bytes large). We will still start receiving as soon
309 * as cbMax is non-zero because:
310 * - it would be quite expensive for pfnCanReceive to accurately
311 * determine free receive buffer space
312 * - if we were waiting for enough free buffers, there is a risk
313 * of deadlocking because the guest could be waiting for a receive
314 * overflow error to allocate more receive buffers
315 */
316 size_t cbMax = pData->pPort->pfnCanReceive(pData->pPort);
317 if (cbMax == 0)
318 {
319 /** @todo receive overflow handling needs serious improving! */
320 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
321 STAM_PROFILE_START(&pData->StatRecvOverflows, b);
322 while ( cbMax == 0
323 && pData->enmState != ASYNCSTATE_TERMINATE)
324 {
325 LogFlow(("drvTAPAsyncIoThread: cbMax=%d cbRead=%d waiting...\n", cbMax, cbRead));
326#if 1
327 /* We get signalled by the network driver. 50ms is just for sanity */
328 ASMAtomicXchgU32(&pData->fOutOfSpace, true);
329 RTSemEventWait(pData->EventOutOfSpace, 50);
330#else
331 RTThreadSleep(1);
332#endif
333 cbMax = pData->pPort->pfnCanReceive(pData->pPort);
334 }
335 ASMAtomicXchgU32(&pData->fOutOfSpace, false);
336 STAM_PROFILE_STOP(&pData->StatRecvOverflows, b);
337 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
338 if (pData->enmState == ASYNCSTATE_TERMINATE)
339 break;
340 }
341
342 /*
343 * Pass the data up.
344 */
345#ifdef LOG_ENABLED
346 uint64_t u64Now = RTTimeProgramNanoTS();
347 LogFlow(("drvTAPAsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
348 cbRead, u64Now, u64Now - pData->u64LastReceiveTS, u64Now - pData->u64LastTransferTS));
349 pData->u64LastReceiveTS = u64Now;
350#endif
351 Log2(("drvTAPAsyncIoThread: cbRead=%#x\n"
352 "%.*Vhxd\n",
353 cbRead, cbRead, achBuf));
354 STAM_COUNTER_INC(&pData->StatPktRecv);
355 STAM_COUNTER_ADD(&pData->StatPktRecvBytes, cbRead);
356 rc = pData->pPort->pfnReceive(pData->pPort, achBuf, cbRead);
357 AssertRC(rc);
358 }
359 else
360 {
361 LogFlow(("drvTAPAsyncIoThread: RTFileRead -> %Vrc\n", rc));
362 if (rc == VERR_INVALID_HANDLE)
363 break;
364 RTThreadYield();
365 }
366 }
367 else if ( rc > 0
368 && aFDs[1].revents)
369 {
370 LogFlow(("drvTAPAsyncIoThread: Control message: enmState=%d revents=%#x\n", pData->enmState, aFDs[1].revents));
371 if (pData->enmState == ASYNCSTATE_TERMINATE)
372 break;
373 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
374 break;
375
376 /* drain the pipe */
377 char ch;
378 size_t cbRead;
379 RTFileRead(pData->PipeRead, &ch, 1, &cbRead);
380 }
381 else
382 {
383 /*
384 * poll() failed for some reason. Yield to avoid eating too much CPU.
385 *
386 * EINTR errors have been seen frequently. They should be harmless, even
387 * if they are not supposed to occur in our setup.
388 */
389 if (errno == EINTR)
390 Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
391 else
392 AssertMsgFailed(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
393 RTThreadYield();
394 }
395 }
396
397 rc = RTSemEventDestroy(pData->EventOutOfSpace);
398 AssertRC(rc);
399
400 LogFlow(("drvTAPAsyncIoThread: returns %Vrc\n", VINF_SUCCESS));
401 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
402 return VINF_SUCCESS;
403}
404
405#else
406/**
407 * Poller callback.
408 */
409static DECLCALLBACK(void) drvTAPPoller(PPDMDRVINS pDrvIns)
410{
411 /* check how much the device/driver can receive now. */
412 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
413 STAM_PROFILE_ADV_START(&pData->StatReceive, a);
414
415 size_t cbMax = pData->pPort->pfnCanReceive(pData->pPort);
416 while (cbMax > 0)
417 {
418 /* check for data to read */
419 struct pollfd aFDs[1];
420 aFDs[0].fd = pData->FileDevice;
421 aFDs[0].events = POLLIN | POLLPRI;
422 aFDs[0].revents = 0;
423 if (poll(&aFDs[0], 1, 0) > 0)
424 {
425 if (aFDs[0].revents & (POLLIN | POLLPRI))
426 {
427 /* data waiting, read it. */
428 char achBuf[4096];
429 size_t cbRead = 0;
430 int rc = RTFileRead(pData->FileDevice, achBuf, RT_MIN(sizeof(achBuf), cbMax), &cbRead);
431 if (VBOX_SUCCESS(rc))
432 {
433 STAM_COUNTER_INC(&pData->StatPktRecv);
434 STAM_COUNTER_ADD(&pData->StatPktRecvBytes, cbRead);
435
436 /* push it up to guy over us. */
437 Log2(("drvTAPPoller: cbRead=%#x\n"
438 "%.*Vhxd\n",
439 cbRead, cbRead, achBuf));
440 rc = pData->pPort->pfnReceive(pData->pPort, achBuf, cbRead);
441 AssertRC(rc);
442 }
443 else
444 AssertRC(rc);
445 if (VBOX_FAILURE(rc) || !cbRead)
446 break;
447 }
448 else
449 break;
450 }
451 else
452 break;
453
454 cbMax = pData->pPort->pfnCanReceive(pData->pPort);
455 }
456
457 STAM_PROFILE_ADV_STOP(&pData->StatReceive, a);
458}
459#endif
460
461
462#if defined(RT_OS_SOLARIS)
463/**
464 * Calls OS-specific TAP setup application/script.
465 *
466 * @returns VBox error code.
467 * @param pData The instance data.
468 */
469static int drvTAPSetupApplication(PDRVTAP pData)
470{
471 char *pszArgs[3];
472 pszArgs[0] = pData->pszSetupApplication;
473 pszArgs[1] = pData->pszDeviceNameActual;
474 pszArgs[2] = NULL;
475
476/** @todo use RTProcCreate */
477
478 Log2(("Starting TAP setup application: %s %s\n", pData->pszSetupApplication, pData->pszDeviceNameActual));
479 pid_t pid = fork();
480 if (pid < 0)
481 {
482 /* Bad. fork() failed! */
483 LogRel(("TAP#%d: Failed to fork() process for running TAP setup application: %s\n", pData->pDrvIns->iInstance,
484 pData->pszSetupApplication, strerror(errno)));
485 return VERR_HOSTIF_INIT_FAILED;
486 }
487 if (pid == 0)
488 {
489 /* Child process. */
490 execv(pszArgs[0], pszArgs);
491 _exit(1);
492 }
493
494 /* Parent process. */
495 int result;
496 while (waitpid(pid, &result, 0) < 0)
497 ;
498 if (!WIFEXITED(result) || WEXITSTATUS(result) != 0)
499 {
500 LogRel(("TAP#%d: Failed to run TAP setup application: %s\n", pData->pDrvIns->iInstance, pData->pszSetupApplication));
501 return VERR_HOSTIF_INIT_FAILED;
502 }
503
504 return VINF_SUCCESS;
505}
506
507
508/**
509 * Calls OS-specific TAP terminate application/script.
510 *
511 * @returns VBox error code.
512 * @param pData The instance data.
513 */
514static int drvTAPTerminateApplication(PDRVTAP pData)
515{
516 char *pszArgs[3];
517 pszArgs[0] = pData->pszTerminateApplication;
518 pszArgs[1] = pData->pszDeviceNameActual;
519 pszArgs[2] = NULL;
520
521/** @todo use RTProcCreate */
522
523 Log2(("Starting TAP terminate application: %s %s\n", pData->pszTerminateApplication, pData->pszDeviceNameActual));
524 pid_t pid = fork();
525 if (pid < 0)
526 {
527 /* Bad. fork() failed! */
528 LogRel(("TAP#%d: Failed to fork() process for running TAP terminate application: %s\n", pData->pDrvIns->iInstance,
529 pData->pszTerminateApplication, strerror(errno)));
530 return VERR_HOSTIF_TERM_FAILED;
531 }
532 if (pid == 0)
533 {
534 /* Child process. */
535 execv(pszArgs[0], pszArgs);
536 _exit(1);
537 }
538
539 /* Parent process. */
540 int result;
541 while (waitpid(pid, &result, 0) < 0)
542 ;
543 if (!WIFEXITED(result) || WEXITSTATUS(result) != 0)
544 {
545 LogRel(("TAP#%d: Failed to run TAP terminate application: %s\n", pData->pDrvIns->iInstance, pData->pszSetupApplication));
546 return VERR_HOSTIF_TERM_FAILED;
547 }
548
549 return VINF_SUCCESS;
550}
551
552#endif /* RT_OS_SOLARIS */
553
554
555#ifdef RT_OS_SOLARIS
556/** From net/if_tun.h, installed by Universal TUN/TAP driver */
557# define TUNNEWPPA (('T'<<16) | 0x0001)
558/** Whether to enable ARP for TAP. */
559# define VBOX_SOLARIS_TAP_ARP 1
560
561/**
562 * Creates/Attaches TAP device to IP.
563 *
564 * @returns VBox error code.
565 * @param pDrvIns The driver instance data.
566 * @param pszDevName Pointer to device name.
567 */
568static DECLCALLBACK(int) SolarisTAPAttach(PPDMDRVINS pDrvIns)
569{
570 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
571 LogFlow(("SolarisTapAttach: pData=%p\n", pData));
572
573
574 int IPFileDes = open("/dev/udp", O_RDWR, 0);
575 if (IPFileDes < 0)
576 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
577 N_("Failed to open /dev/udp. errno=%d"), errno);
578
579 int TapFileDes = open("/dev/tap", O_RDWR, 0);
580 if (TapFileDes < 0)
581 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
582 N_("Failed to open /dev/tap for TAP. errno=%d"), errno);
583
584 /* Use the PPA from the ifname if possible (e.g "tap2", then use 2 as PPA) */
585 int iPPA = -1;
586 if (pData->pszDeviceName)
587 {
588 size_t cch = strlen(pData->pszDeviceName);
589 if (cch > 1 && isdigit(pData->pszDeviceName[cch - 1]) != 0)
590 iPPA = pData->pszDeviceName[cch - 1] - '0';
591 }
592
593 struct strioctl ioIF;
594 ioIF.ic_cmd = TUNNEWPPA;
595 ioIF.ic_len = sizeof(iPPA);
596 ioIF.ic_dp = (char *)(&iPPA);
597 ioIF.ic_timout = 0;
598 iPPA = ioctl(TapFileDes, I_STR, &ioIF);
599 if (iPPA < 0)
600 {
601 close(TapFileDes);
602 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
603 N_("Failed to get new interface. errno=%d"), errno);
604 }
605
606 int InterfaceFD = open("/dev/tap", O_RDWR, 0);
607 if (!InterfaceFD)
608 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
609 N_("Failed to open interface /dev/tap. errno=%d"), errno);
610
611 if (ioctl(InterfaceFD, I_PUSH, "ip") == -1)
612 {
613 close(InterfaceFD);
614 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
615 N_("Failed to push IP. errno=%d"), errno);
616 }
617
618 struct lifreq ifReq;
619 memset(&ifReq, 0, sizeof(ifReq));
620 if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
621 LogRel(("TAP#%d: Failed to get interface flags.\n", pDrvIns->iInstance));
622
623 char szTmp[16];
624 char *pszDevName = pData->pszDeviceName;
625 if (!pData->pszDeviceName || !*pData->pszDeviceName)
626 {
627 RTStrPrintf(szTmp, sizeof(szTmp), "tap%d", iPPA);
628 pszDevName = szTmp;
629 }
630
631 ifReq.lifr_ppa = iPPA;
632 RTStrPrintf (ifReq.lifr_name, sizeof(ifReq.lifr_name), pszDevName);
633
634 if (ioctl(InterfaceFD, SIOCSLIFNAME, &ifReq) == -1)
635 LogRel(("TAP#%d: Failed to set PPA. errno=%d\n", pDrvIns->iInstance, errno));
636
637 if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
638 LogRel(("TAP#%d: Failed to get interface flags after setting PPA. errno=%d\n", pDrvIns->iInstance, errno));
639
640#ifdef VBOX_SOLARIS_TAP_ARP
641 /* Interface */
642 if (ioctl(InterfaceFD, I_PUSH, "arp") == -1)
643 LogRel(("TAP#%d: Failed to push ARP to Interface FD. errno=%d\n", pDrvIns->iInstance, errno));
644
645 /* IP */
646 if (ioctl(IPFileDes, I_POP, NULL) == -1)
647 LogRel(("TAP#%d: Failed I_POP from IP FD. errno=%d\n", pDrvIns->iInstance, errno));
648
649 if (ioctl(IPFileDes, I_PUSH, "arp") == -1)
650 LogRel(("TAP#%d: Failed to push ARP to IP FD. errno=%d\n", pDrvIns->iInstance, errno));
651
652 /* ARP */
653 int ARPFileDes = open("/dev/tap", O_RDWR, 0);
654 if (ARPFileDes < 0)
655 LogRel(("TAP#%d: Failed to open for /dev/tap for ARP. errno=%d", pDrvIns->iInstance, errno));
656
657 if (ioctl(ARPFileDes, I_PUSH, "arp") == -1)
658 LogRel(("TAP#%d: Failed to push ARP to ARP FD. errno=%d\n", pDrvIns->iInstance, errno));
659
660 ioIF.ic_cmd = SIOCSLIFNAME;
661 ioIF.ic_timout = 0;
662 ioIF.ic_len = sizeof(ifReq);
663 ioIF.ic_dp = (char *)&ifReq;
664 if (ioctl(ARPFileDes, I_STR, &ioIF) == -1)
665 LogRel(("TAP#%d: Failed to set interface name to ARP.\n", pDrvIns->iInstance));
666#endif
667
668 /* We must use I_LINK and not I_PLINK as I_PLINK makes the link persistent.
669 * Then we would not be able unlink the interface if we reuse it.
670 * Even 'unplumb' won't work after that.
671 */
672 int IPMuxID = ioctl(IPFileDes, I_LINK, InterfaceFD);
673 if (IPMuxID == -1)
674 {
675 close(InterfaceFD);
676#ifdef VBOX_SOLARIS_TAP_ARP
677 close(ARPFileDes);
678#endif
679 LogRel(("TAP#%d: Cannot link TAP device to IP.\n", pDrvIns->iInstance));
680 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
681 N_("Failed to link TAP device to IP. Check TAP interface name. errno=%d"), errno);
682 }
683
684#ifdef VBOX_SOLARIS_TAP_ARP
685 int ARPMuxID = ioctl(IPFileDes, I_LINK, ARPFileDes);
686 if (ARPMuxID == -1)
687 LogRel(("TAP#%d: Failed to link TAP device to ARP\n", pDrvIns->iInstance));
688
689 close(ARPFileDes);
690#endif
691 close(InterfaceFD);
692
693 /* Reuse ifReq */
694 memset(&ifReq, 0, sizeof(ifReq));
695 RTStrPrintf (ifReq.lifr_name, sizeof(ifReq.lifr_name), pszDevName);
696 ifReq.lifr_ip_muxid = IPMuxID;
697#ifdef VBOX_SOLARIS_TAP_ARP
698 ifReq.lifr_arp_muxid = ARPMuxID;
699#endif
700
701 if (ioctl(IPFileDes, SIOCSLIFMUXID, &ifReq) == -1)
702 {
703#ifdef VBOX_SOLARIS_TAP_ARP
704 ioctl(IPFileDes, I_PUNLINK, ARPMuxID);
705#endif
706 ioctl(IPFileDes, I_PUNLINK, IPMuxID);
707 close(IPFileDes);
708 LogRel(("TAP#%d: Failed to set Mux ID.\n", pDrvIns->iInstance));
709 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
710 N_("Failed to set Mux ID. Check TAP interface name. errno=%d"), errno);
711 }
712
713 pData->FileDevice = (RTFILE)TapFileDes;
714 pData->IPFileDevice = (RTFILE)IPFileDes;
715 pData->pszDeviceNameActual = RTStrDup(pszDevName);
716
717 return VINF_SUCCESS;
718}
719
720#endif /* RT_OS_SOLARIS */
721
722
723/**
724 * Queries an interface to the driver.
725 *
726 * @returns Pointer to interface.
727 * @returns NULL if the interface was not supported by the driver.
728 * @param pInterface Pointer to this interface structure.
729 * @param enmInterface The requested interface identification.
730 * @thread Any thread.
731 */
732static DECLCALLBACK(void *) drvTAPQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
733{
734 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
735 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
736 switch (enmInterface)
737 {
738 case PDMINTERFACE_BASE:
739 return &pDrvIns->IBase;
740 case PDMINTERFACE_NETWORK_CONNECTOR:
741 return &pData->INetworkConnector;
742 default:
743 return NULL;
744 }
745}
746
747
748/**
749 * Destruct a driver instance.
750 *
751 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
752 * resources can be freed correctly.
753 *
754 * @param pDrvIns The driver instance data.
755 */
756static DECLCALLBACK(void) drvTAPDestruct(PPDMDRVINS pDrvIns)
757{
758 LogFlow(("drvTAPDestruct\n"));
759 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
760
761#ifdef ASYNC_NET
762 /*
763 * Terminate the Async I/O Thread.
764 */
765 ASMAtomicXchgSize(&pData->enmState, ASYNCSTATE_TERMINATE);
766 if (pData->Thread != NIL_RTTHREAD)
767 {
768 /* Ensure that it does not spin in the CanReceive loop */
769 if (ASMAtomicXchgU32(&pData->fOutOfSpace, false))
770 RTSemEventSignal(pData->EventOutOfSpace);
771
772 int rc = RTFileWrite(pData->PipeWrite, "", 1, NULL);
773 AssertRC(rc);
774 rc = RTThreadWait(pData->Thread, 5000, NULL);
775 AssertRC(rc);
776 pData->Thread = NIL_RTTHREAD;
777 }
778
779 /*
780 * Terminate the control pipe.
781 */
782 if (pData->PipeWrite != NIL_RTFILE)
783 {
784 int rc = RTFileClose(pData->PipeWrite);
785 AssertRC(rc);
786 pData->PipeWrite = NIL_RTFILE;
787 }
788 if (pData->PipeRead != NIL_RTFILE)
789 {
790 int rc = RTFileClose(pData->PipeRead);
791 AssertRC(rc);
792 pData->PipeRead = NIL_RTFILE;
793 }
794#endif
795
796#ifdef RT_OS_SOLARIS
797 if (pData->pszTerminateApplication)
798 drvTAPTerminateApplication(pData);
799
800 if (pData->IPFileDevice != NIL_RTFILE)
801 {
802 int rc = RTFileClose(pData->IPFileDevice);
803 AssertRC(rc);
804 pData->IPFileDevice = NIL_RTFILE;
805 }
806
807 RTStrFree(pData->pszDeviceNameActual);
808#endif
809 MMR3HeapFree(pData->pszDeviceName);
810 MMR3HeapFree(pData->pszSetupApplication);
811 MMR3HeapFree(pData->pszTerminateApplication);
812}
813
814
815/**
816 * Construct a TAP network transport driver instance.
817 *
818 * @returns VBox status.
819 * @param pDrvIns The driver instance data.
820 * If the registration structure is needed, pDrvIns->pDrvReg points to it.
821 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
822 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
823 * iInstance it's expected to be used a bit in this function.
824 */
825static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
826{
827 PDRVTAP pData = PDMINS2DATA(pDrvIns, PDRVTAP);
828
829 /*
830 * Init the static parts.
831 */
832 pData->pDrvIns = pDrvIns;
833 pData->FileDevice = NIL_RTFILE;
834 pData->pszDeviceName = NULL;
835#ifdef RT_OS_SOLARIS
836 pData->pszDeviceNameActual = NULL;
837 pData->IPFileDevice = NIL_RTFILE;
838#endif
839 pData->pszSetupApplication = NULL;
840 pData->pszTerminateApplication = NULL;
841#ifdef ASYNC_NET
842 pData->Thread = NIL_RTTHREAD;
843 pData->enmState = ASYNCSTATE_RUNNING;
844#endif
845 /* IBase */
846 pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
847 /* INetwork */
848 pData->INetworkConnector.pfnSend = drvTAPSend;
849 pData->INetworkConnector.pfnSetPromiscuousMode = drvTAPSetPromiscuousMode;
850 pData->INetworkConnector.pfnNotifyLinkChanged = drvTAPNotifyLinkChanged;
851 pData->INetworkConnector.pfnNotifyCanReceive = drvTAPNotifyCanReceive;
852
853 /*
854 * Validate the config.
855 */
856 if (!CFGMR3AreValuesValid(pCfgHandle, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication"))
857 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
858
859 /*
860 * Check that no-one is attached to us.
861 */
862 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, NULL);
863 if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
864 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_NO_ATTACH,
865 N_("Configuration error: Cannot attach drivers to the TAP driver!"));
866
867 /*
868 * Query the network port interface.
869 */
870 pData->pPort = (PPDMINETWORKPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_NETWORK_PORT);
871 if (!pData->pPort)
872 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
873 N_("Configuration error: The above device/driver didn't export the network port interface!"));
874
875 /*
876 * Read the configuration.
877 */
878#if defined(RT_OS_SOLARIS) /** @todo Other platforms' TAP code should be moved here from ConsoleImpl & VBoxBFE. */
879 rc = CFGMR3QueryStringAlloc(pCfgHandle, "TAPSetupApplication", &pData->pszSetupApplication);
880 if (VBOX_SUCCESS(rc))
881 {
882 if (!RTPathExists(pData->pszSetupApplication))
883 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
884 N_("Invalid TAP setup program path: %s"), pData->pszSetupApplication);
885 }
886 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
887 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
888
889 rc = CFGMR3QueryStringAlloc(pCfgHandle, "TAPTerminateApplication", &pData->pszTerminateApplication);
890 if (VBOX_SUCCESS(rc))
891 {
892 if (!RTPathExists(pData->pszTerminateApplication))
893 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
894 N_("Invalid TAP terminate program path: %s"), pData->pszTerminateApplication);
895 }
896 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
897 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
898
899
900 rc = CFGMR3QueryStringAlloc(pCfgHandle, "Device", &pData->pszDeviceName);
901 if (VBOX_FAILURE(rc))
902 return PDMDRV_SET_ERROR(pDrvIns, rc,
903 N_("Configuration error: Query for \"Device\" string failed!"));
904
905 /*
906 * Do the setup.
907 */
908 rc = SolarisTAPAttach(pDrvIns);
909 if (VBOX_FAILURE(rc))
910 return rc;
911
912 if (pData->pszSetupApplication)
913 {
914 rc = drvTAPSetupApplication(pData);
915 if (VBOX_FAILURE(rc))
916 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
917 N_("Error running TAP setup application. rc=%d"), rc);
918 }
919
920#else /* !SOLARIS */
921
922 int32_t iFile;
923 rc = CFGMR3QueryS32(pCfgHandle, "FileHandle", &iFile);
924 if (VBOX_FAILURE(rc))
925 return PDMDRV_SET_ERROR(pDrvIns, rc,
926 N_("Configuration error: Query for \"FileHandle\" 32-bit signed integer failed!"));
927 pData->FileDevice = (RTFILE)iFile;
928 if (!RTFileIsValid(pData->FileDevice))
929 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
930 N_("The TAP file handle %RTfile is not valid!"), pData->FileDevice);
931#endif /* !SOLARIS */
932
933 /*
934 * Make sure the descriptor is non-blocking and valid.
935 *
936 * We should actually query if it's a TAP device, but I haven't
937 * found any way to do that.
938 */
939 if (fcntl(pData->FileDevice, F_SETFL, O_NONBLOCK) == -1)
940 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
941 N_("Configuration error: Failed to configure /dev/net/tun. errno=%d"), errno);
942 /** @todo determine device name. This can be done by reading the link /proc/<pid>/fd/<fd> */
943 Log(("drvTAPContruct: %d (from fd)\n", pData->FileDevice));
944 rc = VINF_SUCCESS;
945
946#ifdef ASYNC_NET
947 /*
948 * Create the control pipe.
949 */
950 int fds[2];
951#ifdef RT_OS_L4
952 /* XXX We need to tell the library which interface we are using */
953 fds[0] = vboxrtLinuxFd2VBoxFd(VBOXRT_FT_TAP, 0);
954#endif
955 if (pipe(&fds[0]) != 0) /** @todo RTPipeCreate() or something... */
956 {
957 int rc = RTErrConvertFromErrno(errno);
958 AssertRC(rc);
959 return rc;
960 }
961 pData->PipeRead = fds[0];
962 pData->PipeWrite = fds[1];
963
964 /*
965 * Create the async I/O thread.
966 */
967 rc = RTThreadCreate(&pData->Thread, drvTAPAsyncIoThread, pData, 128*_1K, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "TAP");
968 AssertRCReturn(rc, rc);
969#else
970 /*
971 * Register poller
972 */
973 rc = pDrvIns->pDrvHlp->pfnPDMPollerRegister(pDrvIns, drvTAPPoller);
974 AssertRCReturn(rc, rc);
975#endif
976
977#ifdef VBOX_WITH_STATISTICS
978 /*
979 * Statistics.
980 */
981 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
982 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
983 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
984 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
985 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
986 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
987# ifdef ASYNC_NET
988 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatRecvOverflows, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling packet receive overflows.", "/Drivers/TAP%d/RecvOverflows", pDrvIns->iInstance);
989# endif
990#endif /* VBOX_WITH_STATISTICS */
991
992 return rc;
993}
994
995
996/**
997 * TAP network transport driver registration record.
998 */
999const PDMDRVREG g_DrvHostInterface =
1000{
1001 /* u32Version */
1002 PDM_DRVREG_VERSION,
1003 /* szDriverName */
1004 "HostInterface",
1005 /* pszDescription */
1006 "TAP Network Transport Driver",
1007 /* fFlags */
1008 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1009 /* fClass. */
1010 PDM_DRVREG_CLASS_NETWORK,
1011 /* cMaxInstances */
1012 ~0,
1013 /* cbInstance */
1014 sizeof(DRVTAP),
1015 /* pfnConstruct */
1016 drvTAPConstruct,
1017 /* pfnDestruct */
1018 drvTAPDestruct,
1019 /* pfnIOCtl */
1020 NULL,
1021 /* pfnPowerOn */
1022 NULL,
1023 /* pfnReset */
1024 NULL,
1025 /* pfnSuspend */
1026 NULL, /** @todo Do power on, suspend and resume handlers! */
1027 /* pfnResume */
1028 NULL,
1029 /* pfnDetach */
1030 NULL,
1031 /* pfnPowerOff */
1032 NULL
1033};
1034
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