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;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
* iprt/types.h before sys/param.h - prevents UINT32_C and friends.
*/
#include <iprt/types.h>
#include <sys/param.h>
#undef PVM
#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
#include <VBox/log.h>
#include <VBox/err.h>
#include <VBox/version.h>
#include <iprt/assert.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <iprt/spinlock.h>
#include <iprt/string.h>
#include <iprt/uuid.h>
#include <iprt/alloca.h>
#include <sys/systm.h>
RT_BEGIN_DECLS /* Buggy 10.4 headers, fixed in 10.5. */
#include <sys/kpi_mbuf.h>
RT_END_DECLS
#include <net/ethernet.h>
#include <net/if_ether.h>
#include <net/if_types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/conf.h>
#include <miscfs/devfs/devfs.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
#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
/* 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
#elif defined(RT_OS_OS2)
# 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 *
*******************************************************************************/
RT_BEGIN_DECLS
static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData);
static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData);
RT_END_DECLS
static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Declare the module stuff.
*/
RT_BEGIN_DECLS
extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
KMOD_EXPLICIT_DECL(VBoxNetAdp, VBOX_VERSION_STRING, _start, _stop)
DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxNetAdpDarwinStart;
DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxNetAdpDarwinStop;
DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
RT_END_DECLS
/**
* The (common) global data.
*/
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
static VBOXNETADPGLOBALS g_VBoxNetAdpGlobals;
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
static int g_nCtlDev = -1; /* Major dev number */
static void *g_hCtlDev = 0; /* FS dev handle */
/**
* The character device switch table for the driver.
*/
static struct cdevsw g_ChDev =
{
/*.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.
*/
DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac)
{
#if 0 /* Use a locally administered version of the OUI we use for the guest NICs. */
pMac->au8[0] = 0x08 | 2;
pMac->au8[1] = 0x00;
pMac->au8[2] = 0x27;
#else /* this is what \0vb comes down to. It seems to be unassigned atm. */
pMac->au8[0] = 0;
pMac->au8[1] = 0x76;
pMac->au8[2] = 0x62;
#endif
pMac->au8[3] = 0; /* pThis->uUnit >> 16; */
pMac->au8[4] = 0; /* pThis->uUnit >> 8; */
pMac->au8[5] = pThis->uUnit;
}
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
static void vboxNetAdpDarwinComposeUUID(PVBOXNETADP pThis, PRTUUID pUuid)
{
/* Generate UUID from name and MAC address. */
RTUuidClear(pUuid);
memcpy(pUuid->au8, "vboxnet", 7);
pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
pUuid->Gen.u8ClockSeqLow = pThis->uUnit;
vboxNetAdpComposeMACAddress(pThis, (PRTMAC)pUuid->Gen.au8Node);
}
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
/**
* Reads and retains the host interface handle.
*
* @returns The handle, NULL if detached.
* @param pThis
*/
DECLINLINE(ifnet_t) vboxNetAdpDarwinRetainIfNet(PVBOXNETADP pThis)
{
if (pThis->u.s.pIface)
ifnet_reference(pThis->u.s.pIface);
return pThis->u.s.pIface;
}
/**
* Release the host interface handle previously retained
* by vboxNetAdpDarwinRetainIfNet.
*
* @param pThis The instance.
* @param pIfNet The vboxNetAdpDarwinRetainIfNet return value, NULL is fine.
*/
DECLINLINE(void) vboxNetAdpDarwinReleaseIfNet(PVBOXNETADP pThis, ifnet_t pIfNet)
{
NOREF(pThis);
if (pIfNet)
ifnet_release(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.
*/
static mbuf_t vboxNetAdpDarwinMBufFromSG(PVBOXNETADP pThis, PINTNETSG pSG)
{
/// @todo future? mbuf_how_t How = preemtion enabled ? MBUF_DONTWAIT : MBUF_WAITOK;
mbuf_how_t How = 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.
*/
Assert(pSG->cSegsUsed == 1);
Assert(pSG->cbTotal == pSG->aSegs[0].cb);
Assert(pSG->cbTotal > 0);
/*
* 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! --
*/
Assert(!pSG->pvUserData || pSG->pvUserData == pThis);
Assert(!pSG->pvUserData2);
pSG->pvUserData = pThis;
/*
* 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.
*/
mbuf_t pPkt = NULL;
errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt);
if (!err)
{
/* Skip zero sized memory buffers (paranoia). */
mbuf_t pCur = pPkt;
while (pCur && !mbuf_maxlen(pCur))
pCur = mbuf_next(pCur);
Assert(pCur);
/* Set the required packet header attributes. */
mbuf_pkthdr_setlen(pPkt, pSG->cbTotal);
mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur));
/* Special case the single buffer copy. */
if ( mbuf_next(pCur)
&& mbuf_maxlen(pCur) >= pSG->cbTotal)
{
mbuf_setlen(pCur, pSG->cbTotal);
memcpy(mbuf_data(pCur), pSG->aSegs[0].pv, pSG->cbTotal);
}
else
{
/* Multi buffer copying. */
size_t cbSrc = pSG->cbTotal;
uint8_t const *pbSrc = (uint8_t const *)pSG->aSegs[0].pv;
while (cbSrc > 0 && pCur)
{
size_t cb = mbuf_maxlen(pCur);
if (cbSrc < cb)
cb = cbSrc;
mbuf_setlen(pCur, cb);
memcpy(mbuf_data(pCur), pbSrc, cb);
/* advance */
pbSrc += cb;
cbSrc -= cb;
pCur = mbuf_next(pCur);
}
}
if (!err)
return pPkt;
mbuf_freem(pPkt);
}
else
AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
pSG->pvUserData = NULL;
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.
*/
DECLINLINE(unsigned) vboxNetAdpDarwinMBufCalcSGSegs(PVBOXNETADP pThis, mbuf_t pMBuf, void *pvFrame)
{
NOREF(pThis);
/*
* Count the buffers in the chain.
*/
unsigned cSegs = 0;
for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
if (mbuf_len(pCur))
cSegs++;
else if ( !cSegs
&& pvFrame
&& (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
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.
*/
if (mbuf_pkthdr_len(pMBuf) < 60)
cSegs++;
#endif
#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
/* maximize the number of segments. */
cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs);
#endif
return cSegs ? cSegs : 1;
}
/**
* 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)
{
NOREF(pThis);
pSG->pvOwnerData = NULL;
pSG->pvUserData = NULL;
pSG->pvUserData2 = NULL;
pSG->cUsers = 1;
pSG->fFlags = INTNETSG_FLAGS_TEMP;
pSG->cSegsAlloc = cSegs;
/*
* Walk the chain and convert the buffers to segments.
*/
unsigned iSeg = 0;
pSG->cbTotal = 0;
for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
{
size_t cbSeg = mbuf_len(pCur);
if (cbSeg)
{
void *pvSeg = mbuf_data(pCur);
/* deal with pvFrame */
if (!iSeg && pvFrame && pvFrame != pvSeg)
{
void *pvStart = mbuf_datastart(pMBuf);
uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart;
uintptr_t offSegEnd = offSeg + cbSeg;
Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart;
if (RT_LIKELY(offFrame < offSeg))
{
pvSeg = pvFrame;
cbSeg += offSeg - offFrame;
}
else
AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf)));
pvFrame = NULL;
}
AssertBreak(iSeg < cSegs);
pSG->cbTotal += cbSeg;
pSG->aSegs[iSeg].cb = cbSeg;
pSG->aSegs[iSeg].pv = pvSeg;
pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
iSeg++;
}
/* The pvFrame might be in a now empty buffer. */
else if ( !iSeg
&& pvFrame
&& (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
{
cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame;
pSG->cbTotal += cbSeg;
pSG->aSegs[iSeg].cb = cbSeg;
pSG->aSegs[iSeg].pv = pvFrame;
pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
iSeg++;
pvFrame = NULL;
}
}
Assert(iSeg && iSeg <= cSegs);
pSG->cSegsUsed = 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...
*/
if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
{
AssertReturnVoid(iSeg < cSegs);
static uint8_t const s_abZero[128] = {0};
pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
pSG->cbTotal = 60;
pSG->cSegsUsed++;
}
#endif
#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
/*
* Redistribute the segments.
*/
if (pSG->cSegsUsed < pSG->cSegsAlloc)
{
/* copy the segments to the end. */
int iSrc = pSG->cSegsUsed;
int iDst = pSG->cSegsAlloc;
while (iSrc > 0)
{
iDst--;
iSrc--;
pSG->aSegs[iDst] = pSG->aSegs[iSrc];
}
/* create small segments from the start. */
pSG->cSegsUsed = pSG->cSegsAlloc;
iSrc = iDst;
iDst = 0;
while ( iDst < iSrc
&& iDst < pSG->cSegsAlloc)
{
pSG->aSegs[iDst].Phys = NIL_RTHCPHYS;
pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv;
pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE);
if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb)
{
pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb;
pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb;
}
else if (++iSrc >= pSG->cSegsAlloc)
{
pSG->cSegsUsed = iDst + 1;
break;
}
iDst++;
}
}
#endif
AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg));
}
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
static errno_t vboxNetAdpDarwinOutput(ifnet_t pIface, mbuf_t pMBuf)
{
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
Assert(pThis);
if (vboxNetAdpPrepareToReceive(pThis))
{
unsigned cSegs = vboxNetAdpDarwinMBufCalcSGSegs(pThis, pMBuf, NULL);
if (cSegs < VBOXNETADP_DARWIN_MAX_SEGS)
{
PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
vboxNetAdpDarwinMBufToSG(pThis, pMBuf, NULL, pSG, cSegs, INTNETTRUNKDIR_HOST);
vboxNetAdpReceive(pThis, pSG);
}
else
vboxNetAdpCancelReceive(pThis);
}
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
mbuf_freem_list(pMBuf);
return 0;
}
static void vboxNetAdpDarwinAttachFamily(PVBOXNETADP pThis, protocol_family_t Family)
{
u_int32_t i;
for (i = 0; i < VBOXNETADP_MAX_FAMILIES; i++)
if (pThis->u.s.aAttachedFamilies[i] == 0)
{
pThis->u.s.aAttachedFamilies[i] = Family;
break;
}
}
static void vboxNetAdpDarwinDetachFamily(PVBOXNETADP pThis, protocol_family_t Family)
{
u_int32_t i;
for (i = 0; i < VBOXNETADP_MAX_FAMILIES; i++)
if (pThis->u.s.aAttachedFamilies[i] == Family)
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)
{
PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
Assert(pThis);
vboxNetAdpDarwinAttachFamily(pThis, Family);
LogFlow(("vboxNetAdpAddProto: Family=%d.\n", Family));
return ether_add_proto(pIface, Family, pDemuxDesc, nDesc);
}
static errno_t vboxNetAdpDarwinDelProto(ifnet_t pIface, protocol_family_t Family)
{
PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
Assert(pThis);
LogFlow(("vboxNetAdpDelProto: Family=%d.\n", Family));
vboxNetAdpDarwinDetachFamily(pThis, Family);
return ether_del_proto(pIface, Family);
}
static void vboxNetAdpDarwinDetach(ifnet_t pIface)
{
PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
Assert(pThis);
Log2(("vboxNetAdpDarwinDetach: Signaling detach to vboxNetAdpUnregisterDevice.\n"));
/* Let vboxNetAdpDarwinUnregisterDevice know that the interface has been detached. */
RTSemEventSignal(pThis->u.s.hEvtDetached);
}
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
int vboxNetAdpPortOsXmit(PVBOXNETADP pThis, PINTNETSG pSG, uint32_t fDst)
{
int rc = VINF_SUCCESS;
ifnet_t pIfNet = vboxNetAdpDarwinRetainIfNet(pThis); // Really need a wrapper?
if (pIfNet)
{
/*
* Create a mbuf for the gather list and push it onto the host stack.
*/
mbuf_t pMBuf = vboxNetAdpDarwinMBufFromSG(pThis, pSG);
if (pMBuf)
{
/* This is what IONetworkInterface::inputPacket does. */
unsigned const cbEthHdr = 14;
mbuf_pkthdr_setheader(pMBuf, mbuf_data(pMBuf));
mbuf_pkthdr_setlen(pMBuf, mbuf_pkthdr_len(pMBuf) - cbEthHdr);
mbuf_setdata(pMBuf, (uint8_t *)mbuf_data(pMBuf) + cbEthHdr, mbuf_len(pMBuf) - cbEthHdr);
mbuf_pkthdr_setrcvif(pMBuf, pIfNet); /* will crash without this. */
Log(("vboxNetAdpPortOsXmit: calling ifnet_input()\n"));
errno_t err = ifnet_input(pIfNet, pMBuf, NULL);
if (err)
rc = RTErrConvertFromErrno(err);
}
else
{
Log(("vboxNetAdpPortOsXmit: failed to convert SG to mbuf.\n"));
rc = VERR_NO_MEMORY;
}
vboxNetAdpDarwinReleaseIfNet(pThis, pIfNet);
}
else
Log(("vboxNetAdpPortOsXmit: failed to retain the interface.\n"));
return rc;
}
bool vboxNetAdpPortOsIsPromiscuous(PVBOXNETADP pThis)
{
uint16_t fIf = 0;
ifnet_t pIfNet = vboxNetAdpDarwinRetainIfNet(pThis);
if (pIfNet)
{
/* gather the data */
fIf = ifnet_flags(pIfNet);
vboxNetAdpDarwinReleaseIfNet(pThis, pIfNet);
}
return fIf & IFF_PROMISC;
}
void vboxNetAdpPortOsGetMacAddress(PVBOXNETADP pThis, PRTMAC pMac)
{
*pMac = pThis->u.s.Mac;
}
bool vboxNetAdpPortOsIsHostMac(PVBOXNETADP pThis, PCRTMAC pMac)
{
/* ASSUMES that the MAC address never changes. */
return pThis->u.s.Mac.au16[0] == pMac->au16[0]
&& pThis->u.s.Mac.au16[1] == pMac->au16[1]
&& pThis->u.s.Mac.au16[2] == pMac->au16[2];
}
int vboxNetAdpOsDisconnectIt(PVBOXNETADP pThis)
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
int vboxNetAdpOsConnectIt(PVBOXNETADP pThis)
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
//VBOXNETADP g_vboxnet0;
VBOXNETADP g_aAdapters[VBOXNETADP_MAX_INSTANCES];
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
{
int rc;
struct ifnet_init_params Params;
RTUUID uuid;
struct sockaddr_dl mac;
pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
rc = RTSemEventCreate(&pThis->u.s.hEvtDetached);
if (RT_FAILURE(rc))
{
OSDBGPRINT(("vboxNetAdpOsCreate: failed to create semaphore (rc=%d).\n", rc));
return rc;
}
mac.sdl_len = sizeof(mac);
mac.sdl_family = AF_LINK;
mac.sdl_alen = ETHER_ADDR_LEN;
mac.sdl_nlen = 0;
mac.sdl_slen = 0;
memcpy(LLADDR(&mac), pMACAddress->au8, mac.sdl_alen);
RTStrPrintf(pThis->szName, VBOXNETADP_MAX_NAME_LEN, "%s%d", VBOXNETADP_NAME, pThis->uUnit);
vboxNetAdpDarwinComposeUUID(pThis, &uuid);
Params.uniqueid = uuid.au8;
Params.uniqueid_len = sizeof(uuid);
Params.name = VBOXNETADP_NAME;
Params.unit = pThis->uUnit;
Params.family = IFNET_FAMILY_ETHERNET;
Params.type = IFT_ETHER;
Params.output = vboxNetAdpDarwinOutput;
Params.demux = ether_demux;
Params.add_proto = vboxNetAdpDarwinAddProto;
Params.del_proto = vboxNetAdpDarwinDelProto;
Params.check_multi = ether_check_multi;
Params.framer = ether_frameout;
Params.softc = pThis;
Params.ioctl = (ifnet_ioctl_func)ether_ioctl;
Params.set_bpf_tap = NULL;
Params.detach = vboxNetAdpDarwinDetach;
Params.event = NULL;
Params.broadcast_addr = "\xFF\xFF\xFF\xFF\xFF\xFF";
Params.broadcast_len = ETHER_ADDR_LEN;
errno_t err = ifnet_allocate(&Params, &pThis->u.s.pIface);
if (!err)
{
err = ifnet_attach(pThis->u.s.pIface, &mac);
if (!err)
{
err = ifnet_set_flags(pThis->u.s.pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
if (!err)
{
ifnet_set_mtu(pThis->u.s.pIface, VBOXNETADP_MTU);
return VINF_SUCCESS;
}
else
Log(("vboxNetAdpDarwinRegisterDevice: Failed to set flags (err=%d).\n", err));
ifnet_detach(pThis->u.s.pIface);
}
else
Log(("vboxNetAdpDarwinRegisterDevice: Failed to attach to interface (err=%d).\n", err));
ifnet_release(pThis->u.s.pIface);
}
else
Log(("vboxNetAdpDarwinRegisterDevice: Failed to allocate interface (err=%d).\n", err));
RTSemEventDestroy(pThis->u.s.hEvtDetached);
pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
return RTErrConvertFromErrno(err);
}
void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
{
u_int32_t i;
/* Bring down the interface */
int rc = VINF_SUCCESS;
errno_t err;
AssertPtr(pThis->u.s.pIface);
Assert(pThis->u.s.hEvtDetached != NIL_RTSEMEVENT);
err = ifnet_set_flags(pThis->u.s.pIface, 0, IFF_UP | IFF_RUNNING);
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])
ifnet_detach_protocol(pThis->u.s.pIface, pThis->u.s.aAttachedFamilies[i]);
err = ifnet_detach(pThis->u.s.pIface);
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. */
rc = RTSemEventWait(pThis->u.s.hEvtDetached, VBOXNETADP_DETACH_TIMEOUT);
if (rc == VERR_TIMEOUT)
LogRel(("VBoxAdpDrv: Failed to detach interface %s%d\n.",
VBOXNETADP_NAME, pThis->uUnit));
err = ifnet_release(pThis->u.s.pIface);
if (err)
Log(("vboxNetAdpUnregisterDevice: Failed to release interface (err=%d).\n", err));
RTSemEventDestroy(pThis->u.s.hEvtDetached);
pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
}
int vboxNetAdpCreate (PVBOXNETADP *ppNew)
{
int rc;
unsigned i;
for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
{
PVBOXNETADP pThis = &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));
RTMAC Mac;
vboxNetAdpComposeMACAddress(pThis, &Mac);
rc = vboxNetAdpOsCreate(pThis, &Mac);
if (RT_SUCCESS(rc))
{
*ppNew = pThis;
ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Active);
}
else
{
ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
Log(("vboxNetAdpCreate: vboxNetAdpOsCreate failed with '%Rrc'.\n", rc));
}
return rc;
}
}
Log(("vboxNetAdpCreate: no empty slots!\n"));
/* All slots in adapter array are busy. */
return VERR_OUT_OF_RESOURCES;
}
int vboxNetAdpDestroy (PVBOXNETADP pThis)
{
int rc = VINF_SUCCESS;
if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Active))
return VERR_INTNET_FLT_IF_BUSY;
vboxNetAdpOsDestroy(pThis);
ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
return rc;
}
/**
* Device open. Called on open /dev/vboxnetctl
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
{
char szName[128];
szName[0] = '\0';
proc_name(proc_pid(pProcess), szName, sizeof(szName));
Log(("VBoxNetAdpDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
return 0;
}
/**
* Close device.
*/
static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
{
Log(("VBoxNetAdpDarwinClose: pid=%d\n", proc_pid(pProcess)));
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;
uint32_t cbReq = IOCPARM_LEN(iCmd);
PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)pData;
Log(("VBoxNetAdpDarwinIOCtl: param len %#x; iCmd=%#lx\n", cbReq, iCmd));
switch (IOCBASECMD(iCmd))
{
case IOCBASECMD(VBOXNETADP_CTL_ADD):
if ((IOC_DIRMASK & iCmd) == IOC_OUT)
{
PVBOXNETADP pNew;
rc = vboxNetAdpCreate(&pNew);
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;
}
strncpy(pReq->szName, pNew->szName, sizeof(pReq->szName));
}
}
break;
case IOCBASECMD(VBOXNETADP_CTL_REMOVE):
for (unsigned i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
{
PVBOXNETADP pThis = &g_aAdapters[i];
rc = VERR_NOT_FOUND;
if (strncmp(pThis->szName, pReq->szName, VBOXNETADP_MAX_NAME_LEN) == 0)
if (ASMAtomicReadU32((uint32_t volatile *)&pThis->enmState) == kVBoxNetAdpState_Active)
{
rc = vboxNetAdpDestroy(pThis);
break;
}
}
break;
default:
OSDBGPRINT(("VBoxNetAdpDarwinIOCtl: unknown command %x.\n", IOCBASECMD(iCmd)));
rc = VERR_INVALID_PARAMETER;
break;
}
return RT_SUCCESS(rc) ? 0 : EINVAL;
}
int vboxNetAdpOsInit(PVBOXNETADP pThis)
{
/*
* Init the darwin specific members.
*/
pThis->enmState = kVBoxNetAdpState_Invalid;
pThis->u.s.pIface = NULL;
pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
memset(pThis->u.s.aAttachedFamilies, 0, sizeof(pThis->u.s.aAttachedFamilies));
return VINF_SUCCESS;
}
/**
* Start the kernel module.
*/
static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData)
{
int rc;
/*
* Initialize IPRT and find our module tag id.
* (IPRT is shared with VBoxDrv, it creates the loggers.)
*/
rc = RTR0Init(0);
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.
*/
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
memset(&g_VBoxNetAdpGlobals, 0, sizeof(g_VBoxNetAdpGlobals));
rc = vboxNetAdpInitGlobals(&g_VBoxNetAdpGlobals);
#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]);
}
PVBOXNETADP pVboxnet0;
rc = vboxNetAdpCreate(&pVboxnet0);
if (RT_SUCCESS(rc))
{
g_nCtlDev = cdevsw_add(-1, &g_ChDev);
if (g_nCtlDev < 0)
{
LogRel(("VBoxAdp: failed to register control device."));
rc = VERR_CANT_CREATE;
}
else
{
g_hCtlDev = devfs_make_node(makedev(g_nCtlDev, 0), DEVFS_CHAR,
UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME);
if (!g_hCtlDev)
{
LogRel(("VBoxAdp: failed to create FS node for control device."));
rc = VERR_CANT_CREATE;
}
}
}
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
if (RT_SUCCESS(rc))
{
LogRel(("VBoxAdpDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
return KMOD_RETURN_SUCCESS;
}
LogRel(("VBoxAdpDrv: failed to initialize device extension (rc=%d)\n", rc));
RTR0Term();
}
else
printf("VBoxAdpDrv: failed to initialize IPRT (rc=%d)\n", rc);
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
memset(&g_VBoxNetAdpGlobals, 0, sizeof(g_VBoxNetAdpGlobals));
#endif /* VBOXANETADP_DO_NOT_USE_NETFLT */
return KMOD_RETURN_FAILURE;
}
/**
* Stop the kernel module.
*/
static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData)
{
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!
*/
#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
int rc = vboxNetAdpTryDeleteGlobals(&g_VBoxNetAdpGlobals);
if (RT_FAILURE(rc))
{
Log(("VBoxNetAdpDarwinStop - failed, busy.\n"));
return KMOD_RETURN_FAILURE;
}
/*
* Undo the work done during start (in reverse order).
*/
memset(&g_VBoxNetAdpGlobals, 0, sizeof(g_VBoxNetAdpGlobals));
#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 */
devfs_remove(g_hCtlDev);
cdevsw_remove(g_nCtlDev, &g_ChDev);
#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
RTR0Term();
return KMOD_RETURN_SUCCESS;
}