VBoxNetFlt-darwin.cpp revision 5564cd8c0930563b66d9ebcf709a31ad75bfe709
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * VBoxNetFlt - Network Filter Driver (Host), Darwin Specific Code.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Copyright (C) 2006-2008 Sun Microsystems, Inc.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * available from http://www.virtualbox.org. This file is free software;
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * you can redistribute it and/or modify it under the terms of the GNU
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * General Public License (GPL) as published by the Free Software
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * additional information or have any questions.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync/*******************************************************************************
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync* Header Files *
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync*******************************************************************************/
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Deal with conflicts first.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * PVM - BSD mess, that FreeBSD has correct a long time ago.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * iprt/types.h before sys/param.h - prevents UINT32_C and friends.
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncRT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync/*******************************************************************************
ab93606043a9881487aa83be04191d2f4ea24071vboxsync* Defined Constants And Macros *
ab93606043a9881487aa83be04191d2f4ea24071vboxsync*******************************************************************************/
ab93606043a9881487aa83be04191d2f4ea24071vboxsync/** The maximum number of SG segments.
ab93606043a9881487aa83be04191d2f4ea24071vboxsync * Used to prevent stack overflow and similar bad stuff. */
ab93606043a9881487aa83be04191d2f4ea24071vboxsync/** For testing extremely segmented frames. */
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/*******************************************************************************
d05e58c15346e272284b3db3adae55a8fb8977davboxsync* Internal Functions *
d05e58c15346e272284b3db3adae55a8fb8977davboxsync*******************************************************************************/
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncstatic kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData);
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncstatic kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData);
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/*******************************************************************************
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync* Structures and Typedefs *
d05e58c15346e272284b3db3adae55a8fb8977davboxsync*******************************************************************************/
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync * The mbuf tag data.
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync * We have to associate the ethernet header with each packet we're sending
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync * because things like icmp will inherit the tag it self so the tag along
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync * isn't sufficent to identify our mbufs. For the icmp scenario the ethernet
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync * header naturarlly changes before the packet is send pack, so let check it.
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync /** The ethernet header of the outgoing frame. */
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync/** Pointer to a VBoxNetFlt mbuf tag. */
2e42e0850e182e37277fe28ba5b5d1c37018e783vboxsync/** Pointer to a const VBoxNetFlt mbuf tag. */
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/*******************************************************************************
ab93606043a9881487aa83be04191d2f4ea24071vboxsync* Global Variables *
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync*******************************************************************************/
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Declare the module stuff.
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncextern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncextern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncKMOD_EXPLICIT_DECL(VBoxNetFlt, VBOX_VERSION_STRING, _start, _stop)
ab93606043a9881487aa83be04191d2f4ea24071vboxsyncDECLHIDDEN(kmod_start_func_t *) _realmain = VBoxNetFltDarwinStart;
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncDECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxNetFltDarwinStop;
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * The (common) global data.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/** The unique tag id for this module.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * This is basically a unique string hash that lives on untill reboot.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * It is used for tagging mbufs. */
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/** the offset of the struct ifnet::if_pcount variable. */
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncstatic unsigned g_offIfNetPCount = sizeof(void *) * (1 /*if_softc*/ + 1 /*if_name*/ + 2 /*if_link*/ + 2 /*if_addrhead*/ + 1 /*if_check_multi*/)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync/** Macro for accessing ifnet::if_pcount. */
d05e58c15346e272284b3db3adae55a8fb8977davboxsync#define VBOX_GET_PCOUNT(pIfNet) ( *(int *)((uintptr_t)pIfNet + g_offIfNetPCount) )
ab93606043a9881487aa83be04191d2f4ea24071vboxsync * Start the kernel module.
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncstatic kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData)
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Initialize IPRT and find our module tag id.
ab93606043a9881487aa83be04191d2f4ea24071vboxsync * (IPRT is shared with VBoxDrv, it creates the loggers.)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync errno_t err = mbuf_tag_id_find("org.VirtualBox.kext.VBoxFltDrv", &g_idTag);
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Initialize the globals and connect to the support driver.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * for establishing the connect to the support driver.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
d05e58c15346e272284b3db3adae55a8fb8977davboxsync rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync LogRel(("VBoxFltDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
d05e58c15346e272284b3db3adae55a8fb8977davboxsync LogRel(("VBoxFltDrv: failed to initialize device extension (rc=%d)\n", rc));
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync LogRel(("VBoxFltDrv: mbuf_tag_id_find failed, err=%d\n", err));
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync printf("VBoxFltDrv: failed to initialize IPRT (rc=%d)\n", rc);
d05e58c15346e272284b3db3adae55a8fb8977davboxsync memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * Stop the kernel module.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsyncstatic kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Refuse to unload if anyone is currently using the filter driver.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * This is important as I/O kit / xnu will to be able to do usage
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * tracking for us!
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync int rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * Undo the work done during start (in reverse order).
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * Reads and retains the host interface handle.
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * @returns The handle, NULL if detached.
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * @param pThis
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsyncDECLINLINE(ifnet_t) vboxNetFltDarwinRetainIfNet(PVBOXNETFLTINS pThis)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Be careful here to avoid problems racing the detached callback.
ab93606043a9881487aa83be04191d2f4ea24071vboxsync if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
d05e58c15346e272284b3db3adae55a8fb8977davboxsync pIfNet = (ifnet_t)ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pIfNet);
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * Release the host interface handle previously retained
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * by vboxNetFltDarwinRetainIfNet.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * @param pThis The instance.
2805b95732a8d26015a397626b96049a6e6573e7vboxsync * @param pIfNet The vboxNetFltDarwinRetainIfNet return value, NULL is fine.
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncDECLINLINE(void) vboxNetFltDarwinReleaseIfNet(PVBOXNETFLTINS pThis, ifnet_t pIfNet)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Checks whether this is an mbuf created by vboxNetFltDarwinMBufFromSG,
3e39de9ed8b88fa8907468a77bef44d10049b12avboxsync * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
5964f60ed7fb52a3c4becbe83c9429f9b2f119c2vboxsync * @returns true / false accordingly.
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync * @param pThis The instance.
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync * @param pMBuf The mbuf.
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync * @param pvFrame The frame pointer, optional.
d05e58c15346e272284b3db3adae55a8fb8977davboxsyncDECLINLINE(bool) vboxNetFltDarwinMBufIsOur(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Lookup the tag set by vboxNetFltDarwinMBufFromSG.
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync errno_t err = mbuf_tag_find(pMBuf, g_idTag, 0 /* type */, &cbTagData, (void **)&pTagData);
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync return false;
d05e58c15346e272284b3db3adae55a8fb8977davboxsync AssertReturn(cbTagData == sizeof(*pTagData), false);
d05e58c15346e272284b3db3adae55a8fb8977davboxsync * Dig out the ethernet header from the mbuf.
d05e58c15346e272284b3db3adae55a8fb8977davboxsync PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
d05e58c15346e272284b3db3adae55a8fb8977davboxsync pEthHdr = (PCRTNETETHERHDR)mbuf_pkthdr_header(pMBuf);
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync /* ASSUMING that there is enough data to work on! */
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync if ( pEthHdr->DstMac.au8[0] != pTagData->EthHdr.DstMac.au8[0]
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync || pEthHdr->DstMac.au8[1] != pTagData->EthHdr.DstMac.au8[1]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->DstMac.au8[2] != pTagData->EthHdr.DstMac.au8[2]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->DstMac.au8[3] != pTagData->EthHdr.DstMac.au8[3]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->DstMac.au8[4] != pTagData->EthHdr.DstMac.au8[4]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->DstMac.au8[5] != pTagData->EthHdr.DstMac.au8[5]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->SrcMac.au8[0] != pTagData->EthHdr.SrcMac.au8[0]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->SrcMac.au8[1] != pTagData->EthHdr.SrcMac.au8[1]
d05e58c15346e272284b3db3adae55a8fb8977davboxsync || pEthHdr->SrcMac.au8[2] != pTagData->EthHdr.SrcMac.au8[2]
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync || pEthHdr->SrcMac.au8[3] != pTagData->EthHdr.SrcMac.au8[3]
333e7ab9cd83b32080826d06ac7b1951c684ccb5vboxsync || pEthHdr->SrcMac.au8[4] != pTagData->EthHdr.SrcMac.au8[4]
if (!err)
if (!err)
err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData);
if (!err)
return pPkt;
return NULL;
DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
unsigned cSegs = 0;
cSegs++;
else if ( !cSegs
&& pvFrame
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
cSegs++;
DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
unsigned iSeg = 0;
if (cbSeg)
Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
iSeg++;
else if ( !iSeg
&& pvFrame
iSeg++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
while (iSrc > 0)
iDst--;
iSrc--;
iDst = 0;
iDst++;
if (pIfNet)
static errno_t vboxNetFltDarwinIffIoCtl(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, u_long uCmd, void *pvArg)
return EOPNOTSUPP;
static void vboxNetFltDarwinIffEvent(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, const struct kev_msg *pEvMsg)
if (!err)
Log(("vboxNetFltDarwinIffEvent: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
Log(("vboxNetFltDarwinIffEvent: ifnet_set_promiscuous failed on %s, err=%d (%d)\n", pThis->szName, err, VBOX_GET_PCOUNT(pIfNet)));
if (!err)
Log(("vboxNetFltDarwinIffEvent: fixed IFF_PROMISC on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
Log(("vboxNetFltDarwinIffEvent: online, '%s'. flags=%#x (%d)\n", pThis->szName, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
Log(("vboxNetFltDarwinIffEvent: pThis->u.s.pIfNet=%p pIfNet=%p (%d)\n", pThis->u.s.pIfNet, pIfNet, VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame,
if (!pMBuf)
if (fActive)
if (!fActive)
bool fDropIt = false;
if (fDropIt)
static errno_t vboxNetFltDarwinIffOutput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf)
return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, NULL, INTNETTRUNKDIR_HOST, eProtocol);
static errno_t vboxNetFltDarwinIffInput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf, char **ppchFrame)
return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, *ppchFrame, INTNETTRUNKDIR_WIRE, eProtocol);
if (err)
if (!fRediscovery)
return VERR_INTNET_FLT_IF_NOT_FOUND;
if (!err)
if (pIfNet)
LogRel(("VBoxFltDrv: attached to '%s' / %.*Rhxs\n", pThis->szName, sizeof(pThis->u.s.Mac), &pThis->u.s.Mac));
return rc;
if (pIfNet)
if (pMBuf)
if (err)
if (pMBuf)
if (err)
return rc;
bool fRc = false;
if (pIfNet)
return fRc;
if (pIfNet)
if (fActive)
if (fActive)
if (!err)
if (!err)
if (!err)
if (!err)
Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
Log(("VBoxNetFlt: ifnet_set_promiscuous -> err=%d grr! (%d->%d)\n", err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
else if (!err)
Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
if (err)
LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
return VINF_SUCCESS;
return VINF_SUCCESS;
if (pIfFilter)
if (pIfFilter)
//pThis->u.s.Mac = {0};
return VINF_SUCCESS;