VirtualBox

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

Last change on this file since 40754 was 40282, checked in by vboxsync, 12 years ago

*: gcc-4.7: ~0 => ~0U in initializers (warning: narrowing conversion of -1' from int' to `unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing])

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.2 KB
Line 
1/* $Id: DrvTAP.cpp 40282 2012-02-28 21:02:40Z vboxsync $ */
2/** @file
3 * DrvTAP - Universal TAP network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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 RTStrPrintf (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 RTStrPrintf (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 rc = RTPipeClose(pThis->hPipeWrite); AssertRC(rc);
780 pThis->hPipeWrite = NIL_RTPIPE;
781 rc = RTPipeClose(pThis->hPipeRead); AssertRC(rc);
782 pThis->hPipeRead = NIL_RTPIPE;
783
784#ifdef RT_OS_SOLARIS
785 /** @todo r=bird: This *does* need checking against ConsoleImpl2.cpp if used on non-solaris systems. */
786 if (pThis->hFileDevice != NIL_RTFILE)
787 {
788 int rc = RTFileClose(pThis->hFileDevice);
789 AssertRC(rc);
790 pThis->hFileDevice = NIL_RTFILE;
791 }
792
793 /*
794 * Call TerminateApplication after closing the device otherwise
795 * TerminateApplication would not be able to unplumb it.
796 */
797 if (pThis->pszTerminateApplication)
798 drvTAPTerminateApplication(pThis);
799
800#endif /* RT_OS_SOLARIS */
801
802#ifdef RT_OS_SOLARIS
803 if (!pThis->fStatic)
804 RTStrFree(pThis->pszDeviceName); /* allocated by drvTAPSetupApplication */
805 else
806 MMR3HeapFree(pThis->pszDeviceName);
807#else
808 MMR3HeapFree(pThis->pszDeviceName);
809#endif
810 MMR3HeapFree(pThis->pszSetupApplication);
811 MMR3HeapFree(pThis->pszTerminateApplication);
812
813 /*
814 * Kill the xmit lock.
815 */
816 if (RTCritSectIsInitialized(&pThis->XmitLock))
817 RTCritSectDelete(&pThis->XmitLock);
818
819#ifdef VBOX_WITH_STATISTICS
820 /*
821 * Deregister statistics.
822 */
823 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
824 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
825 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
826 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
827 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
828 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
829#endif /* VBOX_WITH_STATISTICS */
830}
831
832
833/**
834 * Construct a TAP network transport driver instance.
835 *
836 * @copydoc FNPDMDRVCONSTRUCT
837 */
838static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
839{
840 PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
841 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
842
843 /*
844 * Init the static parts.
845 */
846 pThis->pDrvIns = pDrvIns;
847 pThis->hFileDevice = NIL_RTFILE;
848 pThis->pszDeviceName = NULL;
849#ifdef RT_OS_SOLARIS
850 pThis->iIPFileDes = -1;
851 pThis->fStatic = true;
852#endif
853 pThis->pszSetupApplication = NULL;
854 pThis->pszTerminateApplication = NULL;
855
856 /* IBase */
857 pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
858 /* INetwork */
859 pThis->INetworkUp.pfnBeginXmit = drvTAPNetworkUp_BeginXmit;
860 pThis->INetworkUp.pfnAllocBuf = drvTAPNetworkUp_AllocBuf;
861 pThis->INetworkUp.pfnFreeBuf = drvTAPNetworkUp_FreeBuf;
862 pThis->INetworkUp.pfnSendBuf = drvTAPNetworkUp_SendBuf;
863 pThis->INetworkUp.pfnEndXmit = drvTAPNetworkUp_EndXmit;
864 pThis->INetworkUp.pfnSetPromiscuousMode = drvTAPNetworkUp_SetPromiscuousMode;
865 pThis->INetworkUp.pfnNotifyLinkChanged = drvTAPNetworkUp_NotifyLinkChanged;
866
867#ifdef VBOX_WITH_STATISTICS
868 /*
869 * Statistics.
870 */
871 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
872 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
873 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
874 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
875 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
876 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
877#endif /* VBOX_WITH_STATISTICS */
878
879 /*
880 * Validate the config.
881 */
882 if (!CFGMR3AreValuesValid(pCfg, "Device\0InitProg\0TermProg\0FileHandle\0TAPSetupApplication\0TAPTerminateApplication\0MAC"))
883 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES, "");
884
885 /*
886 * Check that no-one is attached to us.
887 */
888 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
889 ("Configuration error: Not possible to attach anything to this driver!\n"),
890 VERR_PDM_DRVINS_NO_ATTACH);
891
892 /*
893 * Query the network port interface.
894 */
895 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
896 if (!pThis->pIAboveNet)
897 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
898 N_("Configuration error: The above device/driver didn't export the network port interface"));
899
900 /*
901 * Read the configuration.
902 */
903 int rc;
904#if defined(RT_OS_SOLARIS) /** @todo Other platforms' TAP code should be moved here from ConsoleImpl & VBoxBFE. */
905 rc = CFGMR3QueryStringAlloc(pCfg, "TAPSetupApplication", &pThis->pszSetupApplication);
906 if (RT_SUCCESS(rc))
907 {
908 if (!RTPathExists(pThis->pszSetupApplication))
909 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
910 N_("Invalid TAP setup program path: %s"), pThis->pszSetupApplication);
911 }
912 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
913 return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
914
915 rc = CFGMR3QueryStringAlloc(pCfg, "TAPTerminateApplication", &pThis->pszTerminateApplication);
916 if (RT_SUCCESS(rc))
917 {
918 if (!RTPathExists(pThis->pszTerminateApplication))
919 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
920 N_("Invalid TAP terminate program path: %s"), pThis->pszTerminateApplication);
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, "Device", &pThis->pszDeviceName);
926 if (RT_FAILURE(rc))
927 pThis->fStatic = false;
928
929 /* Obtain the device name from the setup application (if none was specified). */
930 if (pThis->pszSetupApplication)
931 {
932 rc = drvTAPSetupApplication(pThis);
933 if (RT_FAILURE(rc))
934 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
935 N_("Error running TAP setup application. rc=%d"), rc);
936 }
937
938 /*
939 * Do the setup.
940 */
941 rc = SolarisTAPAttach(pThis);
942 if (RT_FAILURE(rc))
943 return rc;
944
945#else /* !RT_OS_SOLARIS */
946
947 uint64_t u64File;
948 rc = CFGMR3QueryU64(pCfg, "FileHandle", &u64File);
949 if (RT_FAILURE(rc))
950 return PDMDRV_SET_ERROR(pDrvIns, rc,
951 N_("Configuration error: Query for \"FileHandle\" 32-bit signed integer failed"));
952 pThis->hFileDevice = (RTFILE)(uintptr_t)u64File;
953 if (!RTFileIsValid(pThis->hFileDevice))
954 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
955 N_("The TAP file handle %RTfile is not valid"), pThis->hFileDevice);
956#endif /* !RT_OS_SOLARIS */
957
958 /*
959 * Create the transmit lock.
960 */
961 rc = RTCritSectInit(&pThis->XmitLock);
962 AssertRCReturn(rc, rc);
963
964 /*
965 * Make sure the descriptor is non-blocking and valid.
966 *
967 * We should actually query if it's a TAP device, but I haven't
968 * found any way to do that.
969 */
970 if (fcntl(RTFileToNative(pThis->hFileDevice), F_SETFL, O_NONBLOCK) == -1)
971 return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
972 N_("Configuration error: Failed to configure /dev/net/tun. errno=%d"), errno);
973 /** @todo determine device name. This can be done by reading the link /proc/<pid>/fd/<fd> */
974 Log(("drvTAPContruct: %d (from fd)\n", pThis->hFileDevice));
975 rc = VINF_SUCCESS;
976
977 /*
978 * Create the control pipe.
979 */
980 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
981 AssertRCReturn(rc, rc);
982
983 /*
984 * Create the async I/O thread.
985 */
986 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
987 AssertRCReturn(rc, rc);
988
989 return rc;
990}
991
992
993/**
994 * TAP network transport driver registration record.
995 */
996const PDMDRVREG g_DrvHostInterface =
997{
998 /* u32Version */
999 PDM_DRVREG_VERSION,
1000 /* szName */
1001 "HostInterface",
1002 /* szRCMod */
1003 "",
1004 /* szR0Mod */
1005 "",
1006 /* pszDescription */
1007 "TAP Network Transport Driver",
1008 /* fFlags */
1009 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1010 /* fClass. */
1011 PDM_DRVREG_CLASS_NETWORK,
1012 /* cMaxInstances */
1013 ~0U,
1014 /* cbInstance */
1015 sizeof(DRVTAP),
1016 /* pfnConstruct */
1017 drvTAPConstruct,
1018 /* pfnDestruct */
1019 drvTAPDestruct,
1020 /* pfnRelocate */
1021 NULL,
1022 /* pfnIOCtl */
1023 NULL,
1024 /* pfnPowerOn */
1025 NULL,
1026 /* pfnReset */
1027 NULL,
1028 /* pfnSuspend */
1029 NULL, /** @todo Do power on, suspend and resume handlers! */
1030 /* pfnResume */
1031 NULL,
1032 /* pfnAttach */
1033 NULL,
1034 /* pfnDetach */
1035 NULL,
1036 /* pfnPowerOff */
1037 NULL,
1038 /* pfnSoftReset */
1039 NULL,
1040 /* u32EndVersion */
1041 PDM_DRVREG_VERSION
1042};
1043
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use