VirtualBox

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

Last change on this file since 45761 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.5 KB
Line 
1/* $Id: DrvTAP.cpp 45061 2013-03-18 14:09:03Z vboxsync $ */
2/** @file
3 * DrvTAP - Universal TAP network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
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
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DRV_TUN
22#include <VBox/log.h>
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmnetifs.h>
25#include <VBox/vmm/pdmnetinline.h>
26
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/ctype.h>
30#include <iprt/file.h>
31#include <iprt/mem.h>
32#include <iprt/path.h>
33#include <iprt/pipe.h>
34#include <iprt/semaphore.h>
35#include <iprt/string.h>
36#include <iprt/thread.h>
37#include <iprt/uuid.h>
38#ifdef RT_OS_SOLARIS
39# include <iprt/process.h>
40# include <iprt/env.h>
41#endif
42
43#include <sys/ioctl.h>
44#include <sys/poll.h>
45#ifdef RT_OS_SOLARIS
46# include <sys/stat.h>
47# include <sys/ethernet.h>
48# include <sys/sockio.h>
49# include <netinet/in.h>
50# include <netinet/in_systm.h>
51# include <netinet/ip.h>
52# include <netinet/ip_icmp.h>
53# include <netinet/udp.h>
54# include <netinet/tcp.h>
55# include <net/if.h>
56# include <stropts.h>
57# include <fcntl.h>
58# include <stdlib.h>
59# include <stdio.h>
60#else
61# include <sys/fcntl.h>
62#endif
63#include <errno.h>
64#include <unistd.h>
65
66#ifdef RT_OS_L4
67# include <l4/vboxserver/file.h>
68#endif
69
70#include "VBoxDD.h"
71
72
73/*******************************************************************************
74* Structures and Typedefs *
75*******************************************************************************/
76/**
77 * TAP driver instance data.
78 *
79 * @implements PDMINETWORKUP
80 */
81typedef struct DRVTAP
82{
83 /** The network interface. */
84 PDMINETWORKUP INetworkUp;
85 /** The network interface. */
86 PPDMINETWORKDOWN pIAboveNet;
87 /** Pointer to the driver instance. */
88 PPDMDRVINS pDrvIns;
89 /** TAP device file handle. */
90 RTFILE hFileDevice;
91 /** The configured TAP device name. */
92 char *pszDeviceName;
93#ifdef RT_OS_SOLARIS
94 /** IP device file handle (/dev/udp). */
95 int iIPFileDes;
96 /** Whether device name is obtained from setup application. */
97 bool fStatic;
98#endif
99 /** TAP setup application. */
100 char *pszSetupApplication;
101 /** TAP terminate application. */
102 char *pszTerminateApplication;
103 /** The write end of the control pipe. */
104 RTPIPE hPipeWrite;
105 /** The read end of the control pipe. */
106 RTPIPE hPipeRead;
107 /** Reader thread. */
108 PPDMTHREAD pThread;
109
110 /** @todo The transmit thread. */
111 /** Transmit lock used by drvTAPNetworkUp_BeginXmit. */
112 RTCRITSECT XmitLock;
113
114#ifdef VBOX_WITH_STATISTICS
115 /** Number of sent packets. */
116 STAMCOUNTER StatPktSent;
117 /** Number of sent bytes. */
118 STAMCOUNTER StatPktSentBytes;
119 /** Number of received packets. */
120 STAMCOUNTER StatPktRecv;
121 /** Number of received bytes. */
122 STAMCOUNTER StatPktRecvBytes;
123 /** Profiling packet transmit runs. */
124 STAMPROFILE StatTransmit;
125 /** Profiling packet receive runs. */
126 STAMPROFILEADV StatReceive;
127#endif /* VBOX_WITH_STATISTICS */
128
129#ifdef LOG_ENABLED
130 /** The nano ts of the last transfer. */
131 uint64_t u64LastTransferTS;
132 /** The nano ts of the last receive. */
133 uint64_t u64LastReceiveTS;
134#endif
135} DRVTAP, *PDRVTAP;
136
137
138/** Converts a pointer to TAP::INetworkUp to a PRDVTAP. */
139#define PDMINETWORKUP_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_OFFSETOF(DRVTAP, INetworkUp)) )
140
141
142/*******************************************************************************
143* Internal Functions *
144*******************************************************************************/
145#ifdef RT_OS_SOLARIS
146static int SolarisTAPAttach(PDRVTAP pThis);
147#endif
148
149
150
151/**
152 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
153 */
154static DECLCALLBACK(int) drvTAPNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
155{
156 PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
157 int rc = RTCritSectTryEnter(&pThis->XmitLock);
158 if (RT_FAILURE(rc))
159 {
160 /** @todo XMIT thread */
161 rc = VERR_TRY_AGAIN;
162 }
163 return rc;
164}
165
166
167/**
168 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
169 */
170static DECLCALLBACK(int) drvTAPNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
171 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
172{
173 PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
174 Assert(RTCritSectIsOwner(&pThis->XmitLock));
175
176 /*
177 * Allocate a scatter / gather buffer descriptor that is immediately
178 * followed by the buffer space of its single segment. The GSO context
179 * comes after that again.
180 */
181 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
182 + RT_ALIGN_Z(cbMin, 16)
183 + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
184 if (!pSgBuf)
185 return VERR_NO_MEMORY;
186
187 /*
188 * Initialize the S/G buffer and return.
189 */
190 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
191 pSgBuf->cbUsed = 0;
192 pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
193 pSgBuf->pvAllocator = NULL;
194 if (!pGso)
195 pSgBuf->pvUser = NULL;
196 else
197 {
198 pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
199 *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
200 }
201 pSgBuf->cSegs = 1;
202 pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
203 pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
204
205#if 0 /* poison */
206 memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
207#endif
208 *ppSgBuf = pSgBuf;
209 return VINF_SUCCESS;
210}
211
212
213/**
214 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
215 */
216static DECLCALLBACK(int) drvTAPNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
217{
218 PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
219 Assert(RTCritSectIsOwner(&pThis->XmitLock));
220 if (pSgBuf)
221 {
222 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
223 pSgBuf->fFlags = 0;
224 RTMemFree(pSgBuf);
225 }
226 return VINF_SUCCESS;
227}
228
229
230/**
231 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
232 */
233static DECLCALLBACK(int) drvTAPNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
234{
235 PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
236 STAM_COUNTER_INC(&pThis->StatPktSent);
237 STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
238 STAM_PROFILE_START(&pThis->StatTransmit, a);
239
240 AssertPtr(pSgBuf);
241 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
242 Assert(RTCritSectIsOwner(&pThis->XmitLock));
243
244 /* Set an FTM checkpoint as this operation changes the state permanently. */
245 PDMDrvHlpFTSetCheckpoint(pThis->pDrvIns, FTMCHECKPOINTTYPE_NETWORK);
246
247 int rc;
248 if (!pSgBuf->pvUser)
249 {
250#ifdef LOG_ENABLED
251 uint64_t u64Now = RTTimeProgramNanoTS();
252 LogFlow(("drvTAPSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
253 pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
254 pThis->u64LastTransferTS = u64Now;
255#endif
256 Log2(("drvTAPSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
257 "%.*Rhxd\n",
258 pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
259
260 rc = RTFileWrite(pThis->hFileDevice, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, NULL);
261 }
262 else
263 {
264 uint8_t abHdrScratch[256];
265 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
266 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
267 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
268 rc = VINF_SUCCESS;
269 for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
270 {
271 uint32_t cbSegFrame;
272 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
273 iSeg, cSegs, &cbSegFrame);
274 rc = RTFileWrite(pThis->hFileDevice, pvSegFrame, cbSegFrame, NULL);
275 if (RT_FAILURE(rc))
276 break;
277 }
278 }
279
280 pSgBuf->fFlags = 0;
281 RTMemFree(pSgBuf);
282
283 STAM_PROFILE_STOP(&pThis->StatTransmit, a);
284 AssertRC(rc);
285 if (RT_FAILURE(rc))
286 rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
287 return rc;
288}
289
290
291/**
292 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
293 */
294static DECLCALLBACK(void) drvTAPNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
295{
296 PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
297 RTCritSectLeave(&pThis->XmitLock);
298}
299
300
301/**
302 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
303 */
304static DECLCALLBACK(void) drvTAPNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
305{
306 LogFlow(("drvTAPNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
307 /* nothing to do */
308}
309
310
311/**
312 * Notification on link status changes.
313 *
314 * @param pInterface Pointer to the interface structure containing the called function pointer.
315 * @param enmLinkState The new link state.
316 * @thread EMT
317 */
318static DECLCALLBACK(void) drvTAPNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
319{
320 LogFlow(("drvTAPNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
321 /** @todo take action on link down and up. Stop the polling and such like. */
322}
323
324
325/**
326 * Asynchronous I/O thread for handling receive.
327 *
328 * @returns VINF_SUCCESS (ignored).
329 * @param Thread Thread handle.
330 * @param pvUser Pointer to a DRVTAP structure.
331 */
332static DECLCALLBACK(int) drvTAPAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
333{
334 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
335 LogFlow(("drvTAPAsyncIoThread: pThis=%p\n", pThis));
336
337 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
338 return VINF_SUCCESS;
339
340 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
341
342 /*
343 * Polling loop.
344 */
345 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
346 {
347 /*
348 * Wait for something to become available.
349 */
350 struct pollfd aFDs[2];
351 aFDs[0].fd = RTFileToNative(pThis->hFileDevice);
352 aFDs[0].events = POLLIN | POLLPRI;
353 aFDs[0].revents = 0;
354 aFDs[1].fd = RTPipeToNative(pThis->hPipeRead);
355 aFDs[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
356 aFDs[1].revents = 0;
357 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
358 errno=0;
359 int rc = poll(&aFDs[0], RT_ELEMENTS(aFDs), -1 /* infinite */);
360
361 /* this might have changed in the meantime */
362 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
363 break;
364
365 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
366 if ( rc > 0
367 && (aFDs[0].revents & (POLLIN | POLLPRI))
368 && !aFDs[1].revents)
369 {
370 /*
371 * Read the frame.
372 */
373 char achBuf[16384];
374 size_t cbRead = 0;
375 /** @note At least on Linux we will never receive more than one network packet
376 * after poll() returned successfully. I don't know why but a second
377 * RTFileRead() operation will return with VERR_TRY_AGAIN in any case. */
378 rc = RTFileRead(pThis->hFileDevice, achBuf, sizeof(achBuf), &cbRead);
379 if (RT_SUCCESS(rc))
380 {
381 /*
382 * Wait for the device to have space for this frame.
383 * Most guests use frame-sized receive buffers, hence non-zero cbMax
384 * automatically means there is enough room for entire frame. Some
385 * guests (eg. Solaris) use large chains of small receive buffers
386 * (each 128 or so bytes large). We will still start receiving as soon
387 * as cbMax is non-zero because:
388 * - it would be quite expensive for pfnCanReceive to accurately
389 * determine free receive buffer space
390 * - if we were waiting for enough free buffers, there is a risk
391 * of deadlocking because the guest could be waiting for a receive
392 * overflow error to allocate more receive buffers
393 */
394 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
395 int rc1 = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
396 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
397
398 /*
399 * A return code != VINF_SUCCESS means that we were woken up during a VM
400 * state transition. Drop the packet and wait for the next one.
401 */
402 if (RT_FAILURE(rc1))
403 continue;
404
405 /*
406 * Pass the data up.
407 */
408#ifdef LOG_ENABLED
409 uint64_t u64Now = RTTimeProgramNanoTS();
410 LogFlow(("drvTAPAsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
411 cbRead, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
412 pThis->u64LastReceiveTS = u64Now;
413#endif
414 Log2(("drvTAPAsyncIoThread: cbRead=%#x\n" "%.*Rhxd\n", cbRead, cbRead, achBuf));
415 STAM_COUNTER_INC(&pThis->StatPktRecv);
416 STAM_COUNTER_ADD(&pThis->StatPktRecvBytes, cbRead);
417 rc1 = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, achBuf, cbRead);
418 AssertRC(rc1);
419 }
420 else
421 {
422 LogFlow(("drvTAPAsyncIoThread: RTFileRead -> %Rrc\n", rc));
423 if (rc == VERR_INVALID_HANDLE)
424 break;
425 RTThreadYield();
426 }
427 }
428 else if ( rc > 0
429 && aFDs[1].revents)
430 {
431 LogFlow(("drvTAPAsyncIoThread: Control message: enmState=%d revents=%#x\n", pThread->enmState, aFDs[1].revents));
432 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
433 break;
434
435 /* drain the pipe */
436 char ch;
437 size_t cbRead;
438 RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
439 }
440 else
441 {
442 /*
443 * poll() failed for some reason. Yield to avoid eating too much CPU.
444 *
445 * EINTR errors have been seen frequently. They should be harmless, even
446 * if they are not supposed to occur in our setup.
447 */
448 if (errno == EINTR)
449 Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
450 else
451 AssertMsgFailed(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
452 RTThreadYield();
453 }
454 }
455
456
457 LogFlow(("drvTAPAsyncIoThread: returns %Rrc\n", VINF_SUCCESS));
458 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
459 return VINF_SUCCESS;
460}
461
462
463/**
464 * Unblock the send thread so it can respond to a state change.
465 *
466 * @returns VBox status code.
467 * @param pDevIns The pcnet device instance.
468 * @param pThread The send thread.
469 */
470static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
471{
472 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
473
474 size_t cbIgnored;
475 int rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
476 AssertRC(rc);
477
478 return VINF_SUCCESS;
479}
480
481
482#if defined(RT_OS_SOLARIS)
483/**
484 * Calls OS-specific TAP setup application/script.
485 *
486 * @returns VBox error code.
487 * @param pThis The instance data.
488 */
489static int drvTAPSetupApplication(PDRVTAP pThis)
490{
491 char szCommand[4096];
492
493 RTStrPrintf(szCommand, sizeof(szCommand), "%s %s", pThis->pszSetupApplication,
494 pThis->fStatic ? pThis->pszDeviceName : "");
495
496 /* Pipe open the setup application. */
497 Log2(("Starting TAP setup application: %s\n", szCommand));
498 FILE* pfSetupHandle = popen(szCommand, "r");
499 if (pfSetupHandle == 0)
500 {
501 LogRel(("TAP#%d: Failed to run TAP setup application: %s\n", pThis->pDrvIns->iInstance,
502 pThis->pszSetupApplication, strerror(errno)));
503 return VERR_HOSTIF_INIT_FAILED;
504 }
505 if (!pThis->fStatic)
506 {
507 /* Obtain device name from setup application. */
508 char acBuffer[64];
509 size_t cBufSize;
510 fgets(acBuffer, sizeof(acBuffer), pfSetupHandle);
511 cBufSize = strlen(acBuffer);
512 /* The script must return the name of the interface followed by a carriage return as the
513 first line of its output. We need a null-terminated string. */
514 if ((cBufSize < 2) || (acBuffer[cBufSize - 1] != '\n'))
515 {
516 pclose(pfSetupHandle);
517 LogRel(("The TAP interface setup script did not return the name of a TAP device.\n"));
518 return VERR_HOSTIF_INIT_FAILED;
519 }
520 /* Overwrite the terminating newline character. */
521 acBuffer[cBufSize - 1] = 0;
522 RTStrAPrintf(&pThis->pszDeviceName, "%s", acBuffer);
523 }
524 int rc = pclose(pfSetupHandle);
525 if (!WIFEXITED(rc))
526 {
527 LogRel(("The TAP interface setup script terminated abnormally.\n"));
528 return VERR_HOSTIF_INIT_FAILED;
529 }
530 if (WEXITSTATUS(rc) != 0)
531 {
532 LogRel(("The TAP interface setup script returned a non-zero exit code.\n"));
533 return VERR_HOSTIF_INIT_FAILED;
534 }
535 return VINF_SUCCESS;
536}
537
538
539/**
540 * Calls OS-specific TAP terminate application/script.
541 *
542 * @returns VBox error code.
543 * @param pThis The instance data.
544 */
545static int drvTAPTerminateApplication(PDRVTAP pThis)
546{
547 char *pszArgs[3];
548 pszArgs[0] = pThis->pszTerminateApplication;
549 pszArgs[1] = pThis->pszDeviceName;
550 pszArgs[2] = NULL;
551
552 Log2(("Starting TAP terminate application: %s %s\n", pThis->pszTerminateApplication, pThis->pszDeviceName));
553 RTPROCESS pid = NIL_RTPROCESS;
554 int rc = RTProcCreate(pszArgs[0], pszArgs, RTENV_DEFAULT, 0, &pid);
555 if (RT_SUCCESS(rc))
556 {
557 RTPROCSTATUS Status;
558 rc = RTProcWait(pid, 0, &Status);
559 if (RT_SUCCESS(rc))
560 {
561 if ( Status.iStatus == 0
562 && Status.enmReason == RTPROCEXITREASON_NORMAL)
563 return VINF_SUCCESS;
564
565 LogRel(("TAP#%d: Error running TAP terminate application: %s\n", pThis->pDrvIns->iInstance, pThis->pszTerminateApplication));
566 }
567 else
568 LogRel(("TAP#%d: RTProcWait failed for: %s\n", pThis->pDrvIns->iInstance, pThis->pszTerminateApplication));
569 }
570 else
571 {
572 /* Bad. RTProcCreate() failed! */
573 LogRel(("TAP#%d: Failed to fork() process for running TAP terminate application: %s\n", pThis->pDrvIns->iInstance,
574 pThis->pszTerminateApplication, strerror(errno)));
575 }
576 return VERR_HOSTIF_TERM_FAILED;
577}
578
579#endif /* RT_OS_SOLARIS */
580
581
582#ifdef RT_OS_SOLARIS
583/** From net/if_tun.h, installed by Universal TUN/TAP driver */
584# define TUNNEWPPA (('T'<<16) | 0x0001)
585/** Whether to enable ARP for TAP. */
586# define VBOX_SOLARIS_TAP_ARP 1
587
588/**
589 * Creates/Attaches TAP device to IP.
590 *
591 * @returns VBox error code.
592 * @param pThis The instance data.
593 */
594static DECLCALLBACK(int) SolarisTAPAttach(PDRVTAP pThis)
595{
596 LogFlow(("SolarisTapAttach: pThis=%p\n", pThis));
597
598
599 int IPFileDes = open("/dev/udp", O_RDWR, 0);
600 if (IPFileDes < 0)
601 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
602 N_("Failed to open /dev/udp. errno=%d"), errno);
603
604 int TapFileDes = open("/dev/tap", O_RDWR, 0);
605 if (TapFileDes < 0)
606 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
607 N_("Failed to open /dev/tap for TAP. errno=%d"), errno);
608
609 /* Use the PPA from the ifname if possible (e.g "tap2", then use 2 as PPA) */
610 int iPPA = -1;
611 if (pThis->pszDeviceName)
612 {
613 size_t cch = strlen(pThis->pszDeviceName);
614 if (cch > 1 && RT_C_IS_DIGIT(pThis->pszDeviceName[cch - 1]) != 0)
615 iPPA = pThis->pszDeviceName[cch - 1] - '0';
616 }
617
618 struct strioctl ioIF;
619 ioIF.ic_cmd = TUNNEWPPA;
620 ioIF.ic_len = sizeof(iPPA);
621 ioIF.ic_dp = (char *)(&iPPA);
622 ioIF.ic_timout = 0;
623 iPPA = ioctl(TapFileDes, I_STR, &ioIF);
624 if (iPPA < 0)
625 {
626 close(TapFileDes);
627 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
628 N_("Failed to get new interface. errno=%d"), errno);
629 }
630
631 int InterfaceFD = open("/dev/tap", O_RDWR, 0);
632 if (!InterfaceFD)
633 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
634 N_("Failed to open interface /dev/tap. errno=%d"), errno);
635
636 if (ioctl(InterfaceFD, I_PUSH, "ip") == -1)
637 {
638 close(InterfaceFD);
639 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
640 N_("Failed to push IP. errno=%d"), errno);
641 }
642
643 struct lifreq ifReq;
644 memset(&ifReq, 0, sizeof(ifReq));
645 if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
646 LogRel(("TAP#%d: Failed to get interface flags.\n", pThis->pDrvIns->iInstance));
647
648 ifReq.lifr_ppa = iPPA;
649 RTStrCopy(ifReq.lifr_name, sizeof(ifReq.lifr_name), pThis->pszDeviceName);
650
651 if (ioctl(InterfaceFD, SIOCSLIFNAME, &ifReq) == -1)
652 LogRel(("TAP#%d: Failed to set PPA. errno=%d\n", pThis->pDrvIns->iInstance, errno));
653
654 if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
655 LogRel(("TAP#%d: Failed to get interface flags after setting PPA. errno=%d\n", pThis->pDrvIns->iInstance, errno));
656
657#ifdef VBOX_SOLARIS_TAP_ARP
658 /* Interface */
659 if (ioctl(InterfaceFD, I_PUSH, "arp") == -1)
660 LogRel(("TAP#%d: Failed to push ARP to Interface FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
661
662 /* IP */
663 if (ioctl(IPFileDes, I_POP, NULL) == -1)
664 LogRel(("TAP#%d: Failed I_POP from IP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
665
666 if (ioctl(IPFileDes, I_PUSH, "arp") == -1)
667 LogRel(("TAP#%d: Failed to push ARP to IP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
668
669 /* ARP */
670 int ARPFileDes = open("/dev/tap", O_RDWR, 0);
671 if (ARPFileDes < 0)
672 LogRel(("TAP#%d: Failed to open for /dev/tap for ARP. errno=%d", pThis->pDrvIns->iInstance, errno));
673
674 if (ioctl(ARPFileDes, I_PUSH, "arp") == -1)
675 LogRel(("TAP#%d: Failed to push ARP to ARP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
676
677 ioIF.ic_cmd = SIOCSLIFNAME;
678 ioIF.ic_timout = 0;
679 ioIF.ic_len = sizeof(ifReq);
680 ioIF.ic_dp = (char *)&ifReq;
681 if (ioctl(ARPFileDes, I_STR, &ioIF) == -1)
682 LogRel(("TAP#%d: Failed to set interface name to ARP.\n", pThis->pDrvIns->iInstance));
683#endif
684
685 /* We must use I_LINK and not I_PLINK as I_PLINK makes the link persistent.
686 * Then we would not be able unlink the interface if we reuse it.
687 * Even 'unplumb' won't work after that.
688 */
689 int IPMuxID = ioctl(IPFileDes, I_LINK, InterfaceFD);
690 if (IPMuxID == -1)
691 {
692 close(InterfaceFD);
693#ifdef VBOX_SOLARIS_TAP_ARP
694 close(ARPFileDes);
695#endif
696 LogRel(("TAP#%d: Cannot link TAP device to IP.\n", pThis->pDrvIns->iInstance));
697 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
698 N_("Failed to link TAP device to IP. Check TAP interface name. errno=%d"), errno);
699 }
700
701#ifdef VBOX_SOLARIS_TAP_ARP
702 int ARPMuxID = ioctl(IPFileDes, I_LINK, ARPFileDes);
703 if (ARPMuxID == -1)
704 LogRel(("TAP#%d: Failed to link TAP device to ARP\n", pThis->pDrvIns->iInstance));
705
706 close(ARPFileDes);
707#endif
708 close(InterfaceFD);
709
710 /* Reuse ifReq */
711 memset(&ifReq, 0, sizeof(ifReq));
712 RTStrCopy(ifReq.lifr_name, sizeof(ifReq.lifr_name), pThis->pszDeviceName);
713 ifReq.lifr_ip_muxid = IPMuxID;
714#ifdef VBOX_SOLARIS_TAP_ARP
715 ifReq.lifr_arp_muxid = ARPMuxID;
716#endif
717
718 if (ioctl(IPFileDes, SIOCSLIFMUXID, &ifReq) == -1)
719 {
720#ifdef VBOX_SOLARIS_TAP_ARP
721 ioctl(IPFileDes, I_PUNLINK, ARPMuxID);
722#endif
723 ioctl(IPFileDes, I_PUNLINK, IPMuxID);
724 close(IPFileDes);
725 LogRel(("TAP#%d: Failed to set Mux ID.\n", pThis->pDrvIns->iInstance));
726 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
727 N_("Failed to set Mux ID. Check TAP interface name. errno=%d"), errno);
728 }
729
730 int rc = RTFileFromNative(&pThis->hFileDevice, TapFileDes);
731 AssertLogRelRC(rc);
732 if (RT_FAILURE(rc)))
733 {
734 close(IPFileDes);
735 close(TapFileDes);
736 }
737 pThis->iIPFileDes = IPFileDes;
738
739 return VINF_SUCCESS;
740}
741
742#endif /* RT_OS_SOLARIS */
743
744/* -=-=-=-=- PDMIBASE -=-=-=-=- */
745
746/**
747 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
748 */
749static DECLCALLBACK(void *) drvTAPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
750{
751 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
752 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
753
754 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
755 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
756 return NULL;
757}
758
759/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
760
761/**
762 * Destruct a driver instance.
763 *
764 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
765 * resources can be freed correctly.
766 *
767 * @param pDrvIns The driver instance data.
768 */
769static DECLCALLBACK(void) drvTAPDestruct(PPDMDRVINS pDrvIns)
770{
771 LogFlow(("drvTAPDestruct\n"));
772 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
773 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
774
775 /*
776 * Terminate the control pipe.
777 */
778 int rc;
779 if (pThis->hPipeWrite != NIL_RTPIPE)
780 {
781 rc = RTPipeClose(pThis->hPipeWrite); AssertRC(rc);
782 pThis->hPipeWrite = NIL_RTPIPE;
783 }
784 if (pThis->hPipeRead != NIL_RTPIPE)
785 {
786 rc = RTPipeClose(pThis->hPipeRead); AssertRC(rc);
787 pThis->hPipeRead = NIL_RTPIPE;
788 }
789
790#ifdef RT_OS_SOLARIS
791 /** @todo r=bird: This *does* need checking against ConsoleImpl2.cpp if used on non-solaris systems. */
792 if (pThis->hFileDevice != NIL_RTFILE)
793 {
794 int rc = RTFileClose(pThis->hFileDevice); AssertRC(rc);
795 pThis->hFileDevice = NIL_RTFILE;
796 }
797
798 /*
799 * Call TerminateApplication after closing the device otherwise
800 * TerminateApplication would not be able to unplumb it.
801 */
802 if (pThis->pszTerminateApplication)
803 drvTAPTerminateApplication(pThis);
804
805#endif /* RT_OS_SOLARIS */
806
807#ifdef RT_OS_SOLARIS
808 if (!pThis->fStatic)
809 RTStrFree(pThis->pszDeviceName); /* allocated by drvTAPSetupApplication */
810 else
811 MMR3HeapFree(pThis->pszDeviceName);
812#else
813 MMR3HeapFree(pThis->pszDeviceName);
814#endif
815 pThis->pszDeviceName = NULL;
816 MMR3HeapFree(pThis->pszSetupApplication);
817 pThis->pszSetupApplication = NULL;
818 MMR3HeapFree(pThis->pszTerminateApplication);
819 pThis->pszTerminateApplication = NULL;
820
821 /*
822 * Kill the xmit lock.
823 */
824 if (RTCritSectIsInitialized(&pThis->XmitLock))
825 RTCritSectDelete(&pThis->XmitLock);
826
827#ifdef VBOX_WITH_STATISTICS
828 /*
829 * Deregister statistics.
830 */
831 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
832 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
833 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
834 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
835 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
836 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
837#endif /* VBOX_WITH_STATISTICS */
838}
839
840
841/**
842 * Construct a TAP network transport driver instance.
843 *
844 * @copydoc FNPDMDRVCONSTRUCT
845 */
846static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
847{
848 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
849 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
850
851 /*
852 * Init the static parts.
853 */
854 pThis->pDrvIns = pDrvIns;
855 pThis->hFileDevice = NIL_RTFILE;
856 pThis->hPipeWrite = NIL_RTPIPE;
857 pThis->hPipeRead = NIL_RTPIPE;
858 pThis->pszDeviceName = NULL;
859#ifdef RT_OS_SOLARIS
860 pThis->iIPFileDes = -1;
861 pThis->fStatic = true;
862#endif
863 pThis->pszSetupApplication = NULL;
864 pThis->pszTerminateApplication = NULL;
865
866 /* IBase */
867 pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
868 /* INetwork */
869 pThis->INetworkUp.pfnBeginXmit = drvTAPNetworkUp_BeginXmit;
870 pThis->INetworkUp.pfnAllocBuf = drvTAPNetworkUp_AllocBuf;
871 pThis->INetworkUp.pfnFreeBuf = drvTAPNetworkUp_FreeBuf;
872 pThis->INetworkUp.pfnSendBuf = drvTAPNetworkUp_SendBuf;
873 pThis->INetworkUp.pfnEndXmit = drvTAPNetworkUp_EndXmit;
874 pThis->INetworkUp.pfnSetPromiscuousMode = drvTAPNetworkUp_SetPromiscuousMode;
875 pThis->INetworkUp.pfnNotifyLinkChanged = drvTAPNetworkUp_NotifyLinkChanged;
876
877#ifdef VBOX_WITH_STATISTICS
878 /*
879 * Statistics.
880 */
881 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
882 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
883 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
884 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
885 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
886 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
887#endif /* VBOX_WITH_STATISTICS */
888
889 /*
890 * Validate the config.
891 */
892 if (!CFGMR3AreValuesValid(pCfg, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication\0MAC"))
893 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
894
895 /*
896 * Check that no-one is attached to us.
897 */
898 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
899 ("Configuration error: Not possible to attach anything to this driver!\n"),
900 VERR_PDM_DRVINS_NO_ATTACH);
901
902 /*
903 * Query the network port interface.
904 */
905 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
906 if (!pThis->pIAboveNet)
907 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
908 N_("Configuration error: The above device/driver didn't export the network port interface"));
909
910 /*
911 * Read the configuration.
912 */
913 int rc;
914#if defined(RT_OS_SOLARIS) /** @todo Other platforms' TAP code should be moved here from ConsoleImpl. */
915 rc = CFGMR3QueryStringAlloc(pCfg, "TAPSetupApplication", &pThis->pszSetupApplication);
916 if (RT_SUCCESS(rc))
917 {
918 if (!RTPathExists(pThis->pszSetupApplication))
919 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
920 N_("Invalid TAP setup program path: %s"), pThis->pszSetupApplication);
921 }
922 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
923 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
924
925 rc = CFGMR3QueryStringAlloc(pCfg, "TAPTerminateApplication", &pThis->pszTerminateApplication);
926 if (RT_SUCCESS(rc))
927 {
928 if (!RTPathExists(pThis->pszTerminateApplication))
929 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
930 N_("Invalid TAP terminate program path: %s"), pThis->pszTerminateApplication);
931 }
932 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
933 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
934
935 rc = CFGMR3QueryStringAlloc(pCfg, "Device", &pThis->pszDeviceName);
936 if (RT_FAILURE(rc))
937 pThis->fStatic = false;
938
939 /* Obtain the device name from the setup application (if none was specified). */
940 if (pThis->pszSetupApplication)
941 {
942 rc = drvTAPSetupApplication(pThis);
943 if (RT_FAILURE(rc))
944 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
945 N_("Error running TAP setup application. rc=%d"), rc);
946 }
947
948 /*
949 * Do the setup.
950 */
951 rc = SolarisTAPAttach(pThis);
952 if (RT_FAILURE(rc))
953 return rc;
954
955#else /* !RT_OS_SOLARIS */
956
957 uint64_t u64File;
958 rc = CFGMR3QueryU64(pCfg, "FileHandle", &u64File);
959 if (RT_FAILURE(rc))
960 return PDMDRV_SET_ERROR(pDrvIns, rc,
961 N_("Configuration error: Query for \"FileHandle\" 32-bit signed integer failed"));
962 pThis->hFileDevice = (RTFILE)(uintptr_t)u64File;
963 if (!RTFileIsValid(pThis->hFileDevice))
964 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
965 N_("The TAP file handle %RTfile is not valid"), pThis->hFileDevice);
966#endif /* !RT_OS_SOLARIS */
967
968 /*
969 * Create the transmit lock.
970 */
971 rc = RTCritSectInit(&pThis->XmitLock);
972 AssertRCReturn(rc, rc);
973
974 /*
975 * Make sure the descriptor is non-blocking and valid.
976 *
977 * We should actually query if it's a TAP device, but I haven't
978 * found any way to do that.
979 */
980 if (fcntl(RTFileToNative(pThis->hFileDevice), F_SETFL, O_NONBLOCK) == -1)
981 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
982 N_("Configuration error: Failed to configure /dev/net/tun. errno=%d"), errno);
983 /** @todo determine device name. This can be done by reading the link /proc/<pid>/fd/<fd> */
984 Log(("drvTAPContruct: %d (from fd)\n", pThis->hFileDevice));
985 rc = VINF_SUCCESS;
986
987 /*
988 * Create the control pipe.
989 */
990 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
991 AssertRCReturn(rc, rc);
992
993 /*
994 * Create the async I/O thread.
995 */
996 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
997 AssertRCReturn(rc, rc);
998
999 return rc;
1000}
1001
1002
1003/**
1004 * TAP network transport driver registration record.
1005 */
1006const PDMDRVREG g_DrvHostInterface =
1007{
1008 /* u32Version */
1009 PDM_DRVREG_VERSION,
1010 /* szName */
1011 "HostInterface",
1012 /* szRCMod */
1013 "",
1014 /* szR0Mod */
1015 "",
1016 /* pszDescription */
1017 "TAP Network Transport Driver",
1018 /* fFlags */
1019 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1020 /* fClass. */
1021 PDM_DRVREG_CLASS_NETWORK,
1022 /* cMaxInstances */
1023 ~0U,
1024 /* cbInstance */
1025 sizeof(DRVTAP),
1026 /* pfnConstruct */
1027 drvTAPConstruct,
1028 /* pfnDestruct */
1029 drvTAPDestruct,
1030 /* pfnRelocate */
1031 NULL,
1032 /* pfnIOCtl */
1033 NULL,
1034 /* pfnPowerOn */
1035 NULL,
1036 /* pfnReset */
1037 NULL,
1038 /* pfnSuspend */
1039 NULL, /** @todo Do power on, suspend and resume handlers! */
1040 /* pfnResume */
1041 NULL,
1042 /* pfnAttach */
1043 NULL,
1044 /* pfnDetach */
1045 NULL,
1046 /* pfnPowerOff */
1047 NULL,
1048 /* pfnSoftReset */
1049 NULL,
1050 /* u32EndVersion */
1051 PDM_DRVREG_VERSION
1052};
1053
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