VBoxNetAdp-darwin.cpp revision 334052e6de7b55199f6159fa6dfe5bf19591f12b
/* $Id$ */
/** @file
* VBoxNetAdp - Virtual Network Adapter Driver (Host), Darwin Specific Code.
*/
/*
* Copyright (C) 2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
/*
* Deal with conflicts first.
* PVM - BSD mess, that FreeBSD has correct a long time ago.
*/
#define LOG_GROUP LOG_GROUP_NET_TAP_DRV
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
__BEGIN_DECLS /* Buggy 10.4 headers, fixed in 10.5. */
#include <sys/kpi_mbuf.h>
#include <net/ethernet.h>
#include <net/if_ether.h>
#include <net/if_types.h>
#define VBOXNETADP_OS_SPECFIC 1
#include "../VBoxNetAdpInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define VBOXNETADP_MAX_FAMILIES 4
#define VBOXNETADP_NAME "vboxnet"
#define VBOXNETADP_MTU 1500
#define VBOXNETADP_DETACH_TIMEOUT 500
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Declare the module stuff.
*/
/**
* The (common) global data.
*/
static VBOXNETADPGLOBALS g_VBoxNetAdpGlobals;
{
/* Generate UUID from name and MAC address. */
}
/**
* Reads and retains the host interface handle.
*
* @returns The handle, NULL if detached.
* @param pThis
*/
{
return pIfNet;
}
/**
* Release the host interface handle previously retained
* by vboxNetAdpDarwinRetainIfNet.
*
* @param pThis The instance.
* @param pIfNet The vboxNetAdpDarwinRetainIfNet return value, NULL is fine.
*/
{
if (pIfNet)
}
{
{
if (/* converted to SG */0)
else
}
return 0;
}
{
u_int32_t i;
for (i = 0; i < VBOXNETADP_MAX_FAMILIES; i++)
if (pThis->u.s.aAttachedFamilies[i] == 0)
{
break;
}
}
{
u_int32_t i;
for (i = 0; i < VBOXNETADP_MAX_FAMILIES; i++)
pThis->u.s.aAttachedFamilies[i] = 0;
}
static errno_t vboxNetAdpDarwinAddProto(ifnet_t pIface, protocol_family_t Family, const struct ifnet_demux_desc *pDemuxDesc, u_int32_t nDesc)
{
}
{
}
{
Log2(("vboxNetAdpDarwinDetach: Signaling detach to vboxNetAdpUnregisterDevice.\n"));
/* Let vboxNetAdpDarwinUnregisterDevice know that the interface has been detached. */
}
/**
* Internal worker that create a darwin mbuf for a (scatter/)gather list.
*
* Taken from VBoxNetAdp-darwin.cpp.
*
* @returns Pointer to the mbuf.
* @param pThis The instance.
* @param pSG The (scatter/)gather list.
*/
{
/// @todo future? mbuf_how_t How = preemtion enabled ? MBUF_DONTWAIT : MBUF_WAITOK;
/*
* We can't make use of the physical addresses on darwin because the way the
* mbuf / cluster stuffe works (see mbuf_data_to_physical and mcl_to_paddr).
* So, because we're lazy, we will ASSUME that all SGs coming from INTNET
* will only contain one single segment.
*/
/*
* We need some way of getting back to our instance data when
* the mbuf is freed, so use pvUserData for this.
* -- this is not relevant anylonger! --
*/
/*
* Allocate a packet and copy over the data.
*
* Using mbuf_attachcluster() here would've been nice but there are two
* issues with it: (1) it's 10.5.x only, and (2) the documentation indicates
* that it's not supposed to be used for really external buffers. The 2nd
* point might be argued against considering that the only m_clattach user
* is mallocs memory for the ext mbuf and not doing what's stated in the docs.
* However, it's hard to tell if these m_clattach buffers actually makes it
* to the NICs or not, and even if they did, the NIC would need the physical
* addresses for the pages they contain and might end up copying the data
* to a new mbuf anyway.
*
* So, in the end it's better to just do it the simple way that will work
* 100%, even if it involes some extra work (alloc + copy) we really wished
* to avoid.
*/
if (!err)
{
/* Skip zero sized memory buffers (paranoia). */
/* Set the required packet header attributes. */
/* Special case the single buffer copy. */
{
}
else
{
/* Multi buffer copying. */
{
/* advance */
}
}
if (!err)
return pPkt;
}
else
return NULL;
}
/**
* Calculates the number of segments required to represent the mbuf.
*
* Taken from VBoxNetAdp-darwin.cpp.
*
* @returns Number of segments.
* @param pThis The instance.
* @param pMBuf The mbuf.
* @param pvFrame The frame pointer, optional.
*/
{
/*
* Count the buffers in the chain.
*/
unsigned cSegs = 0;
cSegs++;
else if ( !cSegs
&& pvFrame
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
/*
* Add one buffer if the total is less than the ethernet minimum 60 bytes.
* This may allocate a segment too much if the ethernet header is separated,
* but that shouldn't harm us much.
*/
cSegs++;
#endif
/* maximize the number of segments. */
#endif
}
/**
* Initializes a SG list from an mbuf.
*
* Taken from VBoxNetAdp-darwin.cpp.
*
* @returns Number of segments.
* @param pThis The instance.
* @param pMBuf The mbuf.
* @param pSG The SG.
* @param pvFrame The frame pointer, optional.
* @param cSegs The number of segments allocated for the SG.
* This should match the number in the mbuf exactly!
* @param fSrc The source of the frame.
*/
DECLINLINE(void) vboxNetAdpDarwinMBufToSG(PVBOXNETADP pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
{
/*
* Walk the chain and convert the buffers to segments.
*/
unsigned iSeg = 0;
{
if (cbSeg)
{
/* deal with pvFrame */
{
Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
{
}
else
AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
}
iSeg++;
}
/* The pvFrame might be in a now empty buffer. */
else if ( !iSeg
&& pvFrame
{
iSeg++;
}
}
#ifdef PADD_RUNT_FRAMES_FROM_HOST
/*
* Add a trailer if the frame is too small.
*
* Since we're getting to the packet before it is framed, it has not
* yet been padded. The current solution is to add a segment pointing
* to a buffer containing all zeros and pray that works for all frames...
*/
{
}
#endif
/*
* Redistribute the segments.
*/
{
/* copy the segments to the end. */
while (iSrc > 0)
{
iDst--;
iSrc--;
}
/* create small segments from the start. */
iDst = 0;
{
{
}
{
break;
}
iDst++;
}
}
#endif
}
{
int rc = VINF_SUCCESS;
if (pIfNet)
{
/*
* Create a mbuf for the gather list and push it onto the host stack.
*/
if (fDst & INTNETTRUNKDIR_HOST)
{
if (pMBuf)
{
/* This is what IONetworkInterface::inputPacket does. */
unsigned const cbEthHdr = 14;
if (err)
}
else
rc = VERR_NO_MEMORY;
}
}
return rc;
}
{
if (pIfNet)
{
/* gather the data */
}
return fIf & IFF_PROMISC;
}
{
}
{
/* ASSUMES that the MAC address never changes. */
}
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
{
int rc;
struct ifnet_init_params Params;
struct sockaddr_dl mac;
if (RT_FAILURE(rc))
return rc;
if (!err)
{
if (!err)
{
err = ifnet_set_flags(pThis->u.s.pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
if (!err)
{
return VINF_SUCCESS;
}
else
}
else
}
else
return RTErrConvertFromErrno(err);
}
{
u_int32_t i;
/* Bring down the interface */
int rc = VINF_SUCCESS;
if (err)
Log(("vboxNetAdpDarwinUnregisterDevice: Failed to bring down interface "
"(err=%d).\n", err));
/* Detach all protocols. */
for (i = 0; i < VBOXNETADP_MAX_FAMILIES; i++)
if (pThis->u.s.aAttachedFamilies[i])
if (err)
Log(("vboxNetAdpDarwinUnregisterDevice: Failed to detach interface "
"(err=%d).\n", err));
Log2(("vboxNetAdpDarwinUnregisterDevice: Waiting for 'detached' event...\n"));
/* Wait until we get a signal from detach callback. */
if (rc == VERR_TIMEOUT)
LogRel(("VBoxAdpDrv: Failed to detach interface %s%d\n.",
if (err)
}
/**
* Start the kernel module.
*/
{
int rc;
/*
* Initialize IPRT and find our module tag id.
* (IPRT is shared with VBoxDrv, it creates the loggers.)
*/
if (RT_SUCCESS(rc))
{
Log(("VBoxNetAdpDarwinStart\n"));
/*
* Initialize the globals and connect to the support driver.
*
* This will call back vboxNetAdpOsOpenSupDrv (and maybe vboxNetAdpOsCloseSupDrv)
* for establishing the connect to the support driver.
*/
if (RT_SUCCESS(rc))
{
return KMOD_RETURN_SUCCESS;
}
RTR0Term();
}
else
return KMOD_RETURN_FAILURE;
}
/**
* Stop the kernel module.
*/
{
Log(("VBoxNetAdpDarwinStop\n"));
/*
* Refuse to unload if anyone is currently using the filter driver.
* This is important as I/O kit / xnu will to be able to do usage
* tracking for us!
*/
if (RT_FAILURE(rc))
{
Log(("VBoxNetAdpDarwinStop - failed, busy.\n"));
return KMOD_RETURN_FAILURE;
}
/*
* Undo the work done during start (in reverse order).
*/
RTR0Term();
return KMOD_RETURN_SUCCESS;
}