VBoxNetFlt-freebsd.c revision 545f5817b4bf7085bafbc6e5186faa7cabef5102
/* $Id$ */
/** @file
* VBoxNetFlt - Network Filter Driver (Host), FreeBSD Specific Code.
*/
/*
* Copyright (c) 2009 Fredrik Lindberg <fli@shapeshifter.se>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
#include <net/if_types.h>
#include <net/ethernet.h>
#include <netgraph/ng_message.h>
#include <netgraph/netgraph.h>
#include <netgraph/ng_parse.h>
#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
#include <VBox/intnetinline.h>
#include <iprt/initterm.h>
#include <iprt/spinlock.h>
#define VBOXNETFLT_OS_SPECFIC 1
#include "../VBoxNetFltInternal.h"
static int vboxnetflt_modevent(struct module *, int, void *);
static ng_rcvmsg_t ng_vboxnetflt_rcvmsg;
static ng_shutdown_t ng_vboxnetflt_shutdown;
static ng_newhook_t ng_vboxnetflt_newhook;
static ng_rcvdata_t ng_vboxnetflt_rcvdata;
/** Netgraph node type */
#define NG_VBOXNETFLT_NODE_TYPE "vboxnetflt"
/** Netgraph message cookie */
#define NGM_VBOXNETFLT_COOKIE 0x56424f58
/** Input netgraph hook name */
#define NG_VBOXNETFLT_HOOK_IN "input"
/** Output netgraph hook name */
#define NG_VBOXNETFLT_HOOK_OUT "output"
/** mbuf tag identifier */
#define MTAG_VBOX 0x56424f58
/** mbuf packet tag */
#define PACKET_TAG_VBOX 128
/*
* Netgraph command list, we don't support any
* additional commands.
*/
static const struct ng_cmdlist ng_vboxnetflt_cmdlist[] =
{
{ 0 }
};
/*
* Netgraph type definition
*/
static struct ng_type ng_vboxnetflt_typestruct =
{
};
/*
* Use vboxnetflt because the kernel module is named vboxnetflt and vboxnetadp
* depends on this when loading dependencies.
* NETGRAP_INIT will prefix the given name with ng_ so MODULE_DEPEND needs the
* prefixed name.
*/
/**
* The (common) global data.
*/
static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
/**
* Module event handler, called from netgraph subsystem.
*/
{
int rc;
Log(("VBoxNetFltFreeBSDModuleEvent\n"));
switch (enmEventType)
{
case MOD_LOAD:
if (RT_FAILURE(rc))
{
return RTErrConvertToErrno(rc);
}
if (RT_FAILURE(rc))
{
return RTErrConvertToErrno(rc);
}
/* No MODULE_VERSION in ng_ether so we can't MODULE_DEPEND it */
break;
case MOD_UNLOAD:
RTR0Term();
break;
case MOD_SHUTDOWN:
case MOD_QUIESCE:
default:
return EOPNOTSUPP;
}
if (RT_SUCCESS(rc))
return 0;
return RTErrConvertToErrno(rc);
}
/*
* Convert from mbufs to vbox scatter-gather data structure
*/
{
unsigned int i;
{
continue;
i++;
}
#ifdef PADD_RUNT_FRAMES_FROM_HOST
{
i++;
}
#endif
}
/*
* Convert to mbufs from vbox scatter-gather data structure
*/
{
struct mbuf *m;
int error;
unsigned int i;
return (NULL);
if (m == NULL)
return (NULL);
{
if (error == 0)
{
m_freem(m);
return (NULL);
}
}
return (m);
}
{
/* Nothing to do */
return (EINVAL);
}
/*
* Setup netgraph hooks
*/
{
{
#if __FreeBSD_version >= 800000
#endif
}
{
}
else
return (EINVAL);
return (0);
}
/**
* Netgraph message processing for node specific messages.
* We don't accept any special messages so this is not used.
*/
{
int error = 0;
return (EINVAL);
{
default:
}
return (error);
}
/**
* Handle data on netgraph hooks.
* Frames processing is deferred to a taskqueue because this might
* be called with non-sleepable locks held and code paths inside
* the virtual switch might sleep.
*/
{
struct mbuf *m;
bool fActive;
/* Locate tag to see if processing should be skipped for this frame */
{
m_tag_unlink(m, mtag);
}
/*
* Handle incoming hook. This is connected to the
* input path of the interface, thus handling incoming frames.
*/
{
{
ether_demux(ifp, m);
if (fActive)
return (0);
}
}
/*
* Handle mbufs on the outgoing hook, frames going to the interface
*/
{
{
if (fActive)
return rc;
}
}
else
{
m_freem(m);
}
if (fActive)
return (0);
}
{
bool fActive;
/* Prevent node shutdown if we're active */
return (EBUSY);
return (0);
}
{
return (0);
}
/**
* Input processing task, handles incoming frames
*/
{
unsigned int cSegs = 0;
for (;;)
{
if (m == NULL)
break;
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
cSegs++;
#endif
/* Create a copy and deliver to the virtual switch */
fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, INTNETTRUNKDIR_WIRE);
if (fDropIt)
m_freem(m);
else
ether_demux(ifp, m);
}
}
/**
* Output processing task, handles outgoing frames
*/
{
unsigned int cSegs = 0;
for (;;)
{
if (m == NULL)
break;
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
cSegs++;
#endif
/* Create a copy and deliver to the virtual switch */
if (fDropIt)
m_freem(m);
else
ether_output_frame(ifp, m);
}
}
/**
* Called to deliver a frame to either the host, the wire or both.
*/
{
struct mbuf *m;
bool fActive;
int error;
if (fDst & INTNETTRUNKDIR_WIRE)
{
if (m == NULL)
return VERR_NO_MEMORY;
m = m_pullup(m, ETHER_HDR_LEN);
if (m == NULL)
return VERR_NO_MEMORY;
ether_output_frame(ifp, m);
}
if (fDst & INTNETTRUNKDIR_HOST)
{
if (m == NULL)
return VERR_NO_MEMORY;
m = m_pullup(m, ETHER_HDR_LEN);
if (m == NULL)
return VERR_NO_MEMORY;
/*
* Delivering packets to the host will be captured by the
* input hook. Tag the packet with a mbuf tag so that we
* can skip re-delivery of the packet to the guest during
* input hook processing.
*/
{
m_freem(m);
return VERR_NO_MEMORY;
}
m_tag_init(m);
m_tag_prepend(m, mtag);
}
return VINF_SUCCESS;
}
{
/** @todo This isn't taking into account that we put the interface in
* promiscuous mode. */
}
{
char nam[NG_NODESIZ];
return VERR_INTNET_FLT_IF_NOT_FOUND;
/* Create a new netgraph node for this instance */
return VERR_INTERNAL_ERROR;
/* Initialize deferred input queue */
/* Initialize deferred output queue */
/* Attempt to name it vboxnetflt_<ifname> */
/* Report MAC address, promiscuous mode and GSO capabilities. */
/** @todo keep these reports up to date, either by polling for changes or
* intercept some control flow if possible. */
{
pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltFreeBsdIsPromiscuous(pThis));
pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
}
return VINF_SUCCESS;
}
{
/*
* Attempt to check if the interface is still there and re-initialize if
* something has changed.
*/
{
}
{
}
}
{
}
{
return VINF_SUCCESS;
}
{
int error;
struct ngm_connect *con;
struct ngm_rmhook *rm;
char path[NG_PATHSIZ];
/* Activate interface */
if (fActive)
{
/* ng_ether nodes are named after the interface name */
/*
* Send a netgraph connect message to the ng_ether node
* assigned to the bridged interface. Connecting
* the hooks 'lower' (ng_ether) to out 'input'.
*/
sizeof(struct ngm_connect), M_NOWAIT);
return;
/*
* Do the same for the hooks 'upper' (ng_ether) and our
* 'output' hook.
*/
sizeof(struct ngm_connect), M_NOWAIT);
return;
}
else
{
/* De-activate interface */
/* Disconnect msgs are addressed to ourself */
/*
* Send a netgraph message to disconnect our 'input' hook
*/
sizeof(struct ngm_rmhook), M_NOWAIT);
return;
/*
* Send a netgraph message to disconnect our 'output' hook
*/
sizeof(struct ngm_rmhook), M_NOWAIT);
return;
}
}
{
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
}
{
/* Nothing to do */
return VINF_SUCCESS;
}
{
/* Nothing to do */
return VINF_SUCCESS;
}