VBoxNetAdp-darwin.cpp revision 3c095b4909446dd036ca3251b62f023a6754aed0
/* $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_ADP_DRV
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
RT_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 *
*******************************************************************************/
/** The maximum number of SG segments.
* Used to prevent stack overflow and similar bad stuff. */
#define VBOXNETADP_DARWIN_MAX_SEGS 32
#define VBOXNETADP_DARWIN_MAX_FAMILIES 4
#define VBOXNETADP_DARWIN_NAME "vboxnet"
#define VBOXNETADP_DARWIN_MTU 1500
#define VBOXNETADP_DARWIN_DETACH_TIMEOUT 500
/* debug printf */
#if defined(RT_OS_WINDOWS)
# define OSDBGPRINT(a) DbgPrint a
#elif defined(RT_OS_LINUX)
# define OSDBGPRINT(a) printk a
#elif defined(RT_OS_DARWIN)
# define OSDBGPRINT(a) printf a
# define OSDBGPRINT(a) SUPR0Printf a
#elif defined(RT_OS_FREEBSD)
# define OSDBGPRINT(a) printf a
#elif defined(RT_OS_SOLARIS)
# define OSDBGPRINT(a) SUPR0Printf a
#else
# define OSDBGPRINT(a)
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Declare the module stuff.
*/
/**
* The (common) global data.
*/
static VBOXNETADPGLOBALS g_VBoxNetAdpGlobals;
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
static void *g_hCtlDev = 0; /* FS dev handle */
/**
* The character device switch table for the driver.
*/
{
/*.d_open = */VBoxNetAdpDarwinOpen,
/*.d_close = */VBoxNetAdpDarwinClose,
/*.d_read = */eno_rdwrt,
/*.d_write = */eno_rdwrt,
/*.d_ioctl = */VBoxNetAdpDarwinIOCtl,
/*.d_stop = */eno_stop,
/*.d_reset = */eno_reset,
/*.d_ttys = */NULL,
/*.d_select = */eno_select,
/*.d_mmap = */eno_mmap,
/*.d_strategy = */eno_strat,
/*.d_getc = */eno_getc,
/*.d_putc = */eno_putc,
/*.d_type = */0
};
/**
* Generate a suitable MAC address.
*
* @param pThis The instance.
* @param pMac Where to return the MAC address.
*/
{
#if 0 /* Use a locally administered version of the OUI we use for the guest NICs. */
#else /* this is what \0vb comes down to. It seems to be unassigned atm. */
#endif
}
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
{
/* Generate UUID from name and MAC address. */
}
/**
* Reads and retains the host interface handle.
*
* @returns The handle, NULL if detached.
* @param pThis
*/
{
}
/**
* Release the host interface handle previously retained
* by vboxNetAdpDarwinRetainIfNet.
*
* @param pThis The instance.
* @param pIfNet The vboxNetAdpDarwinRetainIfNet return value, NULL is fine.
*/
{
if (pIfNet)
}
/**
* 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
}
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
{
{
if (cSegs < VBOXNETADP_DARWIN_MAX_SEGS)
{
}
else
}
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
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. */
}
{
int rc = VINF_SUCCESS;
if (pIfNet)
{
/*
* Create a mbuf for the gather list and push it onto the host stack.
*/
if (pMBuf)
{
/* This is what IONetworkInterface::inputPacket does. */
unsigned const cbEthHdr = 14;
Log(("vboxNetAdpPortOsXmit: calling ifnet_input()\n"));
if (err)
}
else
{
Log(("vboxNetAdpPortOsXmit: failed to convert SG to mbuf.\n"));
rc = VERR_NO_MEMORY;
}
}
else
Log(("vboxNetAdpPortOsXmit: failed to retain the interface.\n"));
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;
}
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
//VBOXNETADP g_vboxnet0;
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
{
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)
}
{
int rc;
unsigned i;
for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
{
if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Invalid))
{
/* Found an empty slot -- use it. */
Log(("vboxNetAdpCreate: found empty slot: %d\n", i));
if (RT_SUCCESS(rc))
{
}
else
{
}
return rc;
}
}
Log(("vboxNetAdpCreate: no empty slots!\n"));
/* All slots in adapter array are busy. */
return VERR_OUT_OF_RESOURCES;
}
{
int rc = VINF_SUCCESS;
if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Active))
return VERR_INTNET_FLT_IF_BUSY;
return rc;
}
/**
* Device open. Called on open /dev/vboxnetctl
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
char szName[128];
szName[0] = '\0';
return 0;
}
/**
* Close device.
*/
{
return 0;
}
/**
* Device I/O Control entry point.
*
* @returns Darwin for slow IOCtls and VBox status code for the fast ones.
* @param Dev The device number (major+minor).
* @param iCmd The IOCtl command.
* @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
* @param fFlags Flag saying we're a character device (like we didn't know already).
* @param pProcess The process issuing this request.
*/
static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
{
int rc = VINF_SUCCESS;
switch (IOCBASECMD(iCmd))
{
case IOCBASECMD(VBOXNETADP_CTL_ADD):
{
if (RT_SUCCESS(rc))
{
if (cbReq < sizeof(VBOXNETADPREQ))
{
OSDBGPRINT(("VBoxNetAdpDarwinIOCtl: param len %#x < req size %#x; iCmd=%#lx\n", cbReq, sizeof(VBOXNETADPREQ), iCmd));
return EINVAL;
}
}
}
break;
case IOCBASECMD(VBOXNETADP_CTL_REMOVE):
for (unsigned i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
{
rc = VERR_NOT_FOUND;
{
break;
}
}
break;
default:
break;
}
}
{
/*
* Init the darwin specific members.
*/
return VINF_SUCCESS;
}
/**
* 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.
*/
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
for (unsigned i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
{
g_aAdapters[i].uUnit = i;
vboxNetAdpOsInit(&g_aAdapters[i]);
}
if (RT_SUCCESS(rc))
{
if (g_nCtlDev < 0)
{
LogRel(("VBoxAdp: failed to register control device."));
}
else
{
if (!g_hCtlDev)
{
LogRel(("VBoxAdp: failed to create FS node for control device."));
}
}
}
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
if (RT_SUCCESS(rc))
{
return KMOD_RETURN_SUCCESS;
}
RTR0Term();
}
else
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
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).
*/
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
/* Remove virtual adapters */
for (unsigned i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
vboxNetAdpDestroy(&g_aAdapters[i]);
/* Remove control device */
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
RTR0Term();
return KMOD_RETURN_SUCCESS;
}