VirtualBox

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

Last change on this file since 47006 was 46035, checked in by vboxsync, 12 years ago

Remove L4 support from main tree.

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