/* $Id: VBoxNetAdp-darwin.cpp 16976 2009-02-20 16:58:33Z vboxsync $ */ /** @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 #include #undef PVM #define LOG_GROUP LOG_GROUP_NET_TAP_DRV #include #include #include #include #include #include #include #include #include __BEGIN_DECLS /* Buggy 10.4 headers, fixed in 10.5. */ #include __END_DECLS #include #include #include #include #include #include #include #include #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 #define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface)) /******************************************************************************* * Internal Functions * *******************************************************************************/ __BEGIN_DECLS static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData); static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData); __END_DECLS /******************************************************************************* * Global Variables * *******************************************************************************/ /** * Declare the module stuff. */ __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__; __END_DECLS /** * The (common) global data. */ static VBOXNETADPGLOBALS g_VBoxNetAdpGlobals; 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); } /** * Reads and retains the host interface handle. * * @returns The handle, NULL if detached. * @param pThis */ DECLINLINE(ifnet_t) vboxNetAdpDarwinRetainIfNet(PVBOXNETADP pThis) { RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER; ifnet_t pIfNet = NULL; RTSpinlockAcquire(pThis->hSpinlock, &Tmp); if (pThis->enmState >= kVBoxNetAdpState_Connected) ifnet_reference(pThis->u.s.pIface); RTSpinlockRelease(pThis->hSpinlock, &Tmp); return pIfNet; } /** * 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); } static errno_t vboxNetAdpDarwinOutput(ifnet_t pIface, mbuf_t pMBuf) { PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface); Assert(pThis); if (vboxNetAdpPrepareToReceive(pThis)) { if (/* converted to SG */0) vboxNetAdpReceive(pThis, NULL); else vboxNetAdpCancelReceive(pThis); } 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); } /** * 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)); } 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. */ if (fDst & INTNETTRUNKDIR_HOST) { 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. */ errno_t err = ifnet_input(pIfNet, pMBuf, NULL); if (err) rc = RTErrConvertFromErrno(err); } else rc = VERR_NO_MEMORY; } vboxNetAdpDarwinReleaseIfNet(pThis, pIfNet); } 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; } 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)) 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); 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)); 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; } /** * 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. */ memset(&g_VBoxNetAdpGlobals, 0, sizeof(g_VBoxNetAdpGlobals)); rc = vboxNetAdpInitGlobals(&g_VBoxNetAdpGlobals); 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); memset(&g_VBoxNetAdpGlobals, 0, sizeof(g_VBoxNetAdpGlobals)); 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! */ 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)); RTR0Term(); return KMOD_RETURN_SUCCESS; }