VBoxNetFlt-solaris.c revision 83c0265455b213aace04548fe8da23544736c0e1
/* $Id$ */
/** @file
* VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
*/
/*
* Copyright (C) 2008 Sun Microsystems, Inc.
*
* Sun Microsystems, Inc. confidential
* All rights reserved
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#if defined(DEBUG_ramshankar) && !defined(LOG_ENABLED)
# define LOG_ENABLED
#endif
#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
#include <VBox/log.h>
#include <VBox/err.h>
#include <VBox/cdefs.h>
#include <iprt/string.h>
#include <iprt/initterm.h>
#include <iprt/assert.h>
#include <iprt/alloca.h>
#include <iprt/net.h>
#include <iprt/mem.h>
#include <iprt/thread.h>
#include <inet/ip.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/kstr.h>
#include <sys/file.h>
#include <sys/sockio.h>
#include <sys/strsubr.h>
#include <sys/pathname.h>
#include <sys/t_kuser.h>
#include <sys/types.h>
#include <sys/dlpi.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ethernet.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
// Workaround for very strange define in sys/user.h
// #define u (curproc->p_user) /* user is now part of proc structure */
#ifdef u
#undef u
#endif
#define VBOXNETFLT_OS_SPECFIC 1
#include "../VBoxNetFltInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The module name. */
#define DEVICE_NAME "vboxflt"
/** The module descriptions as seen in 'modinfo'. */
#define DEVICE_DESC_DRV "VirtualBox NetFilter Driver"
#define DEVICE_DESC_MOD "VirtualBox NetFilter Module"
/** @todo Remove the below hackery once done! */
#if defined(DEBUG_ramshankar) && defined(LOG_ENABLED)
# undef Log
# define Log LogRel
# undef LogFlow
# define LogFlow LogRel
#endif
/** Dynamic module binding specific oddities. */
#define VBOXNETFLT_IFNAME_LEN LIFNAMSIZ + 1
/*******************************************************************************
* Global Functions *
*******************************************************************************/
/**
* Stream Driver hooks.
*/
static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
/**
* Stream Module hooks.
*/
static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred);
static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fFile, cred_t *pCred);
static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg);
static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg);
static int VBoxNetFltSolarisModWriteService(queue_t *pQueue);
/**
* OS specific hooks invoked from common VBoxNetFlt ring-0.
*/
bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis);
void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac);
bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac);
void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive);
int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis);
int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis);
void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis);
int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis);
int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis);
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Streams: module info.
*/
static struct module_info g_VBoxNetFltSolarisModInfo =
{
0xbad, /* module id */
DEVICE_NAME,
0, /* min. packet size */
INFPSZ, /* max. packet size */
0, /* hi-water mask */
0 /* lo-water mask */
};
/**
* Streams: read queue hooks.
*/
static struct qinit g_VBoxNetFltSolarisReadQ =
{
VBoxNetFltSolarisModReadPut,
NULL, /* service */
VBoxNetFltSolarisModOpen,
VBoxNetFltSolarisModClose,
NULL, /* admin (reserved) */
&g_VBoxNetFltSolarisModInfo,
NULL /* module stats */
};
/**
* Streams: write queue hooks.
*/
static struct qinit g_VBoxNetFltSolarisWriteQ =
{
VBoxNetFltSolarisModWritePut,
VBoxNetFltSolarisModWriteService,
NULL, /* open */
NULL, /* close */
NULL, /* admin (reserved) */
&g_VBoxNetFltSolarisModInfo,
NULL /* module stats */
};
/**
* Streams: IO stream tab.
*/
static struct streamtab g_VBoxNetFltSolarisStreamTab =
{
&g_VBoxNetFltSolarisReadQ,
&g_VBoxNetFltSolarisWriteQ,
NULL, /* muxread init */
NULL /* muxwrite init */
};
/**
* cb_ops: driver char/block entry points
*/
static struct cb_ops g_VBoxNetFltSolarisCbOps =
{
nulldev, /* cb open */
nulldev, /* cb close */
nodev, /* b strategy */
nodev, /* b dump */
nodev, /* b print */
nodev, /* cb read */
nodev, /* cb write */
nodev, /* cb ioctl */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
nochpoll, /* c poll */
ddi_prop_op, /* property ops */
&g_VBoxNetFltSolarisStreamTab,
D_NEW | D_MP | D_MTPERMOD, /* compat. flag */
CB_REV /* revision */
};
/**
* dev_ops: driver entry/exit and other ops.
*/
static struct dev_ops g_VBoxNetFltSolarisDevOps =
{
DEVO_REV, /* driver build revision */
0, /* ref count */
VBoxNetFltSolarisGetInfo,
nulldev, /* identify */
nulldev, /* probe */
VBoxNetFltSolarisAttach,
VBoxNetFltSolarisDetach,
nodev, /* reset */
&g_VBoxNetFltSolarisCbOps,
(struct bus_ops *)0,
nodev /* power */
};
/**
* modldrv: export driver specifics to kernel
*/
static struct modldrv g_VBoxNetFltSolarisDriver =
{
&mod_driverops, /* extern from kernel */
DEVICE_DESC_DRV,
&g_VBoxNetFltSolarisDevOps
};
/**
* fmodsw: streams module ops
*/
static struct fmodsw g_VBoxNetFltSolarisModOps =
{
DEVICE_NAME,
&g_VBoxNetFltSolarisStreamTab,
D_NEW | D_MP | D_MTPERMOD
};
/**
* modlstrmod: streams module specifics to kernel
*/
static struct modlstrmod g_VBoxNetFltSolarisModule =
{
&mod_strmodops, /* extern from kernel */
DEVICE_DESC_MOD,
&g_VBoxNetFltSolarisModOps
};
/**
* modlinkage: export install/remove/info to the kernel
*/
static struct modlinkage g_VBoxNetFltSolarisModLinkage =
{
MODREV_1, /* loadable module system revision */
&g_VBoxNetFltSolarisDriver, /* streams driver framework */
&g_VBoxNetFltSolarisModule, /* streams module framework */
NULL /* terminate array of linkage structures */
};
struct vboxnetflt_state_t;
/**
* vboxnetflt_dladdr_t: DL SAP address format
*/
typedef struct vboxnetflt_dladdr_t
{
ether_addr_t Mac;
uint16_t SAP;
} vboxnetflt_dladdr_t;
#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t)
/**
* which stream is this?
*/
typedef enum VBOXNETFLTSTREAMTYPE
{
kUndefined = 0,
kIpStream = 0x1b,
kArpStream = 0xab
} VBOXNETFLTSTREAMTYPE;
/**
* vboxnetflt_stream_t: per-stream data
*/
typedef struct vboxnetflt_stream_t
{
int DevMinor; /* minor device no. (for clone) */
queue_t *pReadQueue; /* read side queue */
queue_t *pArpReadQueue; /* ARP read queue */
struct vboxnetflt_state_t *pState; /* state associated with this queue */
struct vboxnetflt_stream_t *pNext; /* next stream in list */
bool fPromisc; /* cached promiscous value */
bool fRawMode; /* whether raw mode request was successful */
uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */
t_uscalar_t UnAckPrim; /* unacknowledged primitive sent by this stream, again fake DL request tracking stuff */
PVBOXNETFLTINS pThis; /* the backend instance */
VBOXNETFLTSTREAMTYPE Type; /* the type of the stream Ip/Arp */
} vboxnetflt_stream_t;
/**
* vboxnetflt_state_t: per-driver data
*/
typedef struct vboxnetflt_state_t
{
/** Global device info handle. */
dev_info_t *pDip;
/** The list of all opened streams. */
vboxnetflt_stream_t *pOpenedStreams;
/**
* pCurInstance is the currently VBox instance to be associated with the stream being created
* in ModOpen. This is just shared global data between the dynamic attach and the ModOpen procedure.
*/
PVBOXNETFLTINS pCurInstance;
/** Goes along with pCurInstance to determine type of stream being opened/created. */
VBOXNETFLTSTREAMTYPE CurType;
} vboxnetflt_state_t;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int vboxNetFltSolarisSetRawMode(queue_t *pQueue);
static int vboxNetFltSolarisSetFastMode(queue_t *pQueue);
static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue);
static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg);
static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg);
static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg);
static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg);
static PVBOXNETFLTINS vboxNetFltSolarisFindInstance(vboxnetflt_stream_t *pStream);
static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg);
static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The (common) global data. */
static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
/** Global state. */
static vboxnetflt_state_t g_VBoxNetFltSolarisState;
/** GCC C++ hack. */
unsigned __gxx_personality_v0 = 0xdecea5ed;
/** Global Mutex protecting global state. */
RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
/**
* Kernel entry points
*/
int _init(void)
{
LogFlow((DEVICE_NAME ":_init\n"));
/*
* Prevent module autounloading.
*/
modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
if (pModCtl)
pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
else
LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
/*
* Initialize IPRT.
*/
int rc = RTR0Init(0);
if (RT_SUCCESS(rc))
{
/*
* Initialize Solaris specific globals here.
*/
g_VBoxNetFltSolarisState.pOpenedStreams = NULL;
g_VBoxNetFltSolarisState.pCurInstance = NULL;
rc = RTSemFastMutexCreate(&g_VBoxNetFltSolarisMtx);
if (RT_SUCCESS(rc))
{
/*
* Initialize the globals and connect to the support driver.
*
* This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
* for establishing the connect to the support driver.
*/
memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
rc = vboxNetFltInitGlobals(&g_VBoxNetFltSolarisGlobals);
if (RT_SUCCESS(rc))
{
rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
if (!rc)
return rc;
LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals);
}
else
LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
}
else
LogRel((DEVICE_NAME ":failed to create mutex.\n"));
RTR0Term();
}
else
LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
return -1;
}
int _fini(void)
{
int rc;
LogFlow((DEVICE_NAME ":_fini\n"));
/*
* Undo the work done during start (in reverse order).
*/
rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals);
if (RT_FAILURE(rc))
{
LogRel((DEVICE_NAME ":_fini - busy!\n"));
return EBUSY;
}
if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX)
{
RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
}
RTR0Term();
return mod_remove(&g_VBoxNetFltSolarisModLinkage);
}
int _info(struct modinfo *pModInfo)
{
LogFlow((DEVICE_NAME ":_info\n"));
int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
LogFlow((DEVICE_NAME ":_info returns %d\n", rc));
return rc;
}
/**
* Attach entry point, to attach a device to the system or resume it.
*
* @param pDip The module structure instance.
* @param enmCmd Operation type (attach/resume).
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
switch (enmCmd)
{
case DDI_ATTACH:
{
int instance = ddi_get_instance(pDip);
int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, CLONE_DEV);
if (rc == DDI_SUCCESS)
{
g_VBoxNetFltSolarisState.pDip = pDip;
ddi_report_dev(pDip);
return DDI_SUCCESS;
}
else
LogRel((DEVICE_NAME ":VBoxNetFltSolarisAttach failed to create minor node. rc%d\n", rc));
return DDI_FAILURE;
}
case DDI_RESUME:
{
/* Nothing to do here... */
return DDI_SUCCESS;
}
}
return DDI_FAILURE;
}
/**
* Detach entry point, to detach a device to the system or suspend it.
*
* @param pDip The module structure instance.
* @param enmCmd Operation type (detach/suspend).
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
switch (enmCmd)
{
case DDI_DETACH:
{
int instance = ddi_get_instance(pDip);
ddi_remove_minor_node(pDip, NULL);
return DDI_SUCCESS;
}
case DDI_RESUME:
{
/* Nothing to do here... */
return DDI_SUCCESS;
}
}
return DDI_FAILURE;
}
/**
* Info entry point, called by solaris kernel for obtaining driver info.
*
* @param pDip The module structure instance (do not use).
* @param enmCmd Information request type.
* @param pvArg Type specific argument.
* @param ppvResult Where to store the requested info.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppResult)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd,
getminor((dev_t)pvArg)));
switch (enmCmd)
{
case DDI_INFO_DEVT2DEVINFO:
{
*ppResult = g_VBoxNetFltSolarisState.pDip;
return DDI_SUCCESS;
}
case DDI_INFO_DEVT2INSTANCE:
{
int instance = getminor((dev_t)pvArg);
*ppResult = (void *)(uintptr_t)instance;
return DDI_SUCCESS;
}
}
return DDI_FAILURE;
}
/**
* Stream module open entry point, initializes the queue and allows streams processing.
*
* @param pQueue Pointer to the queue (cannot be NULL).
* @param pDev Pointer to the dev_t associated with the driver at the end of the stream.
* @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
* @param fStreamMode Stream open mode.
* @param pCred Pointer to user credentials.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fOpenMode, int fStreamMode, cred_t *pCred)
{
Assert(pQueue);
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev,
fOpenMode, fStreamMode));
/*
* Already open?
*/
if (pQueue->q_ptr)
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid open.\n"));
return ENOENT;
}
/*
* Check for the VirtualBox instance.
*/
vboxnetflt_state_t *pState = &g_VBoxNetFltSolarisState;
if (RT_UNLIKELY(!pState->pCurInstance))
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n"));
return ENOENT;
}
/*
* Check VirtualBox stream type.
*/
if (pState->CurType == kUndefined)
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode.\n"));
return ENOENT;
}
/*
* Get minor number. For clone opens provide a new dev_t.
*/
minor_t DevMinor = 0;
vboxnetflt_stream_t *pStream = NULL;
vboxnetflt_stream_t **ppPrevStream = &pState->pOpenedStreams;
if (fStreamMode == CLONEOPEN)
{
for (; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
{
if (DevMinor < pStream->DevMinor)
break;
DevMinor++;
}
*pDev = makedevice(getmajor(*pDev), DevMinor);
}
else
DevMinor = getminor(*pDev);
/*
* Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
*/
pStream = RTMemAlloc(sizeof(*pStream));
if (RT_UNLIKELY(!pStream))
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n"));
return ENOMEM;
}
pStream->DevMinor = DevMinor;
pStream->pReadQueue = pQueue;
pStream->pArpReadQueue = NULL;
pStream->pState = pState;
pStream->fPromisc = false;
pStream->fRawMode = false;
/*
* Pick up the current global VBOXNETFLTINS instance as
* the one that we will associate this stream with.
*/
pStream->pThis = pState->pCurInstance;
pStream->Type = pState->CurType;
if (pState->CurType == kIpStream)
pState->pCurInstance->u.s.pvStream = pStream;
else
pState->pCurInstance->u.s.pvArpStream = pStream;
pStream->ModeReqId = 0;
pStream->UnAckPrim = 0;
pQueue->q_ptr = pStream;
WR(pQueue)->q_ptr = pStream;
/*
* Link it to the list of streams.
*/
pStream->pNext = *ppPrevStream;
*ppPrevStream = pStream;
qprocson(pQueue);
/*
* Request the physical address (we cache the acknowledgement).
*/
if (pStream->Type == kIpStream)
vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue);
/*
* Enable raw mode.
*/
if (pStream->Type == kIpStream)
vboxNetFltSolarisSetRawMode(pStream->pReadQueue);
pStream->pThis->fDisconnectedFromHost = false;
NOREF(fOpenMode);
NOREF(pCred);
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen returns 0, DevMinor=%d pQueue=%p\n", DevMinor, pStream->pReadQueue));
return 0;
}
/**
* Stream module close entry point, undoes the work done on open and closes the stream.
*
* @param pQueue Pointer to the queue (cannot be NULL).
* @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
* @param pCred Pointer to user credentials.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fOpenMode, cred_t *pCred)
{
Assert(pQueue);
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode));
vboxnetflt_stream_t *pStream = NULL;
vboxnetflt_stream_t **ppPrevStream = NULL;
vboxnetflt_state_t *pState = NULL;
/*
* Get instance data.
*/
pStream = (vboxnetflt_stream_t *)pQueue->q_ptr;
if (RT_UNLIKELY(!pStream))
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModClose failed to get stream.\n"));
return ENXIO;
}
pState = pStream->pState;
Assert(pState);
/*
* Enable native mode.
*/
if (pStream->Type == kIpStream)
vboxNetFltSolarisSetFastMode(pStream->pReadQueue);
pStream->pThis->fDisconnectedFromHost = true;
qprocsoff(pQueue);
/*
* Unlink it from the list of streams.
*/
for (ppPrevStream = &pState->pOpenedStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr)
break;
*ppPrevStream = pStream->pNext;
/*
* Delete the stream.
*/
if (pStream->Type == kIpStream)
pStream->pThis->u.s.pvStream = NULL;
else
pStream->pThis->u.s.pvArpStream = NULL;
RTMemFree(pStream);
pQueue->q_ptr = NULL;
WR(pQueue)->q_ptr = NULL;
NOREF(fOpenMode);
NOREF(pCred);
return 0;
}
/**
* Read side put procedure for processing messages in the read queue.
*
* @param pQueue Pointer to the queue.
* @param pMsg Pointer to the message.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n"));
bool fSendUpstream = true;
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
/*
* In the unlikely case where VirtualBox crashed and this filter
* is somehow still in the host stream we must try not to panic the host.
*/
if ( pStream
&& pStream->Type == kIpStream
&& pMsg)
{
PVBOXNETFLTINS pThis = vboxNetFltSolarisFindInstance(pStream);
if (RT_LIKELY(pThis))
{
switch (DB_TYPE(pMsg))
{
case M_DATA:
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n"));
vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
fSendUpstream = false; /* vboxNetFltSolarisRecv would send it if required, do nothing more here. */
break;
}
case M_PROTO:
case M_PCPROTO:
{
union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
t_uscalar_t Prim = pPrim->dl_primitive;
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim));
switch (Prim)
{
case DL_UNITDATA_IND:
{
/*
* I do not think control would come here... We convert all outgoing fast mode requests
* to raw mode; so I don't think we should really receive any fast mode replies.
*/
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_UNITDATA_IND\n"));
vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
fSendUpstream = false; /* vboxNetFltSolarisRecv would send it if required, do nothing more here. */
break;
}
case DL_PHYS_ADDR_ACK:
{
vboxNetFltSolarisCachePhysAddr(pThis, pMsg);
/*
* Swallow our fake physical address request acknowledgement.
*/
if (pStream->UnAckPrim == DL_PHYS_ADDR_REQ)
{
freemsg(pMsg);
fSendUpstream = false;
}
break;
}
case DL_OK_ACK:
{
dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr;
if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n"));
pStream->fPromisc = true;
}
else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n"));
pStream->fPromisc = false;
}
/*
* Swallow our fake promiscous request acknowledgement.
*/
if (pStream->UnAckPrim == pOkAck->dl_correct_primitive)
{
freemsg(pMsg);
fSendUpstream = false;
}
break;
}
}
break;
}
case M_IOCACK:
{
/*
* Swallow our fake raw/fast path mode request acknowledgement.
*/
struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
if (pIOC->ioc_id == pStream->ModeReqId)
{
pStream->fRawMode = !pStream->fRawMode;
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n",
pStream->fRawMode ? "ON" : "OFF"));
freemsg(pMsg);
fSendUpstream = false;
}
break;
}
case M_FLUSH:
{
/*
* We must support flushing queues.
*/
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n"));
if (*pMsg->b_rptr & FLUSHR)
flushq(pQueue, FLUSHALL);
break;
}
}
}
else
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n"));
}
if ( fSendUpstream
&& pMsg)
{
/*
* Pass foward high priority messages or when there's no flow control
* on an empty queue, otherwise queue them.
*/
if ( queclass(pMsg) == QPCTL
|| (pQueue->q_first == NULL && canputnext(pQueue)))
{
putnext(pQueue, pMsg);
}
else
putq(pQueue, pMsg);
}
return 0;
}
/**
* Write side put procedure for processing messages in the write queue.
*
* @param pQueue Pointer to the queue.
* @param pMsg Pointer to the message.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg));
/*
* Check for the VirtualBox connection.
*/
bool fSendDownstream = true;
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
/*
* In the unlikely case where VirtualBox crashed and this filter
* is somehow still in the host stream we must try not to panic the host.
*/
if ( pStream
&& pStream->Type == kIpStream
&& pMsg)
{
PVBOXNETFLTINS pThis = vboxNetFltSolarisFindInstance(pStream);
if (RT_LIKELY(pThis))
{
switch (DB_TYPE(pMsg))
{
case M_DATA:
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut M_DATA\n"));
break;
}
case M_PROTO:
case M_PCPROTO:
{
/*
* If we are not yet in raw mode; just pass through.
*/
if (!pStream->fRawMode)
break;
/*
* Queue up other primitives to the service routine.
*/
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut M_PROTO/M_PCPROTO\n"));
union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
t_uscalar_t Prim = pPrim->dl_primitive;
switch (Prim)
{
case DL_UNITDATA_REQ:
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut DL_UNITDATA_REQ\n"));
mblk_t *pRawMsg;
int rc = vboxNetFltSolarisUnitDataToRaw(pThis, pMsg, &pRawMsg);
if (RT_SUCCESS(rc))
pMsg = pRawMsg;
else
fSendDownstream = false;
break;
}
default:
{
/*
* Enqueue other DLPI primitives and service them later.
*/
fSendDownstream = false;
putq(pQueue, pMsg);
break;
}
}
break;
}
case M_IOCTL:
{
struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
if (pIOC->ioc_cmd == DL_IOC_HDR_INFO)
{
if (pThis->fActive)
{
fSendDownstream = false;
/*
* Some sanity checks.
*/
if ( !pMsg->b_cont
|| (MBLKL(pMsg->b_cont) < sizeof(dl_unitdata_req_t) + VBOXNETFLT_DLADDRL)
|| (*((uint32_t *)pMsg->b_cont->b_rptr) != DL_UNITDATA_REQ))
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModWritePut: Invalid fast path request!\n"));
miocnak(pQueue, pMsg, 0, EPROTO);
break;
}
dl_unitdata_req_t *pDlReq = (dl_unitdata_req_t *)pMsg->b_cont->b_rptr;
size_t cbOffset = pDlReq->dl_dest_addr_offset;
size_t cbAddr = pDlReq->dl_dest_addr_length;
if ( !MBLKIN(pMsg->b_cont, cbOffset, cbAddr)
|| cbAddr != VBOXNETFLT_DLADDRL)
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModWritePut: Invalid header in fast path request!\n"));
miocnak(pQueue, pMsg, 0, EPROTO);
break;
}
vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_cont->b_rptr + cbOffset);
mblk_t *pReplyMsg = allocb(sizeof(RTNETETHERHDR), BPRI_MED);
if (RT_UNLIKELY(!pReplyMsg))
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModWritePut: Invalid header in fast path request!\n"));
miocnak(pQueue, pMsg, 0, ENOMEM);
break;
}
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
bcopy(&pDLSapAddr->Mac, &pEthHdr->DstMac, sizeof(RTMAC));
bcopy(&pThis->u.s.Mac, &pEthHdr->SrcMac, sizeof(RTMAC));
pEthHdr->EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
linkb(pMsg, pReplyMsg);
/*
* Somebody is wanting fast path when we need raw mode.
* Since we are evil, let's acknowledge the request ourselves!
*/
miocack(pQueue, pMsg, msgsize(pMsg->b_cont), EINVAL);
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut: Fast path request acknowledged.\n"));
}
}
break;
}
case M_FLUSH:
{
/*
* Canonical flush courtesy man qreply(9F) while we have a service routine.
*/
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut M_FLUSH\n"));
if (*pMsg->b_rptr & FLUSHW)
{
/*
* Flush and mark as serviced.
*/
flushq(pQueue, FLUSHALL);
*pMsg->b_rptr &= ~FLUSHW;
}
if (*pMsg->b_rptr & FLUSHR)
{
/*
* Send the request upstream.
*/
flushq(RD(pQueue), FLUSHALL);
qreply(pQueue, pMsg);
}
else
freemsg(pMsg);
break;
}
}
}
else
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModWritePut: Could not find VirtualBox instance!!\n"));
}
if ( fSendDownstream
&& pMsg)
{
/*
* Pass foward high priority messages or when there's no flow control
* on an empty queue, otherwise queue them.
*/
if ( queclass(pMsg) == QPCTL
|| (pQueue->q_first == NULL && canputnext(pQueue)))
{
putnext(pQueue, pMsg);
}
else
putq(pQueue, pMsg);
}
return 0;
}
/**
* Write side service procedure for deferred message processing on the write queue.
*
* @param pQueue Pointer to the queue.
*
* @returns corresponding solaris error code.
*/
static int VBoxNetFltSolarisModWriteService(queue_t *pQueue)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWriteService pQueue=%p\n", pQueue));
/*
* Implment just the flow controlled service draining of the queue.
* Nothing else to do here, we handle all the important stuff in the Put procedure.
*/
mblk_t *pMsg;
while (pMsg = getq(pQueue))
{
if (canputnext(pQueue))
putnext(pQueue, pMsg);
else
{
putbq(pQueue, pMsg);
break;
}
}
return 0;
}
/**
* Put the stream in raw mode.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
*/
static int vboxNetFltSolarisSetRawMode(queue_t *pQueue)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pQueue=%p\n", pQueue));
mblk_t *pRawMsg = NULL;
pRawMsg = mkiocb(DLIOCRAW);
if (RT_UNLIKELY(!pRawMsg))
return VERR_NO_MEMORY;
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr;
pStream->ModeReqId = pIOC->ioc_id;
pIOC->ioc_count = 0;
qreply(pQueue, pRawMsg);
return VINF_SUCCESS;
}
/**
* Put the stream back in fast path mode.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
*/
static int vboxNetFltSolarisSetFastMode(queue_t *pQueue)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetFastMode pQueue=%p\n", pQueue));
mblk_t *pFastMsg = mkiocb(DL_IOC_HDR_INFO);
if (RT_UNLIKELY(!pFastMsg))
return VERR_NO_MEMORY;
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
struct iocblk *pIOC = (struct iocblk *)pFastMsg->b_rptr;
pStream->ModeReqId = pIOC->ioc_id;
size_t cbReq = sizeof(dl_unitdata_req_t) + sizeof(vboxnetflt_dladdr_t);
mblk_t *pDataReqMsg = allocb(cbReq, BPRI_MED);
if (RT_UNLIKELY(!pDataReqMsg))
return VERR_NO_MEMORY;
DB_TYPE(pDataReqMsg) = M_PROTO;
dl_unitdata_req_t *pDataReq = (dl_unitdata_req_t *)pDataReqMsg->b_rptr;
pDataReq->dl_primitive = DL_UNITDATA_REQ;
pDataReq->dl_dest_addr_length = sizeof(vboxnetflt_dladdr_t);
pDataReq->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
pDataReq->dl_priority.dl_min = 0;
pDataReq->dl_priority.dl_max = 0;
bzero(pDataReqMsg->b_rptr + sizeof(dl_unitdata_req_t), sizeof(vboxnetflt_dladdr_t));
pDataReqMsg->b_wptr = pDataReqMsg->b_rptr + cbReq;
/*
* Link the data format request message into the header ioctl message.
*/
pFastMsg->b_cont = pDataReqMsg;
pIOC->ioc_count = msgdsize(pDataReqMsg);
qreply(pQueue, pFastMsg);
return VINF_SUCCESS;
}
/**
* Send fake promiscous mode requests downstream.
*
* @param pQueue Pointer to the queue.
* @param fPromisc Whether to enable promiscous mode or not.
* @param PromiscLevel Promiscous level; DL_PROMISC_PHYS/SAP/MULTI.
*
* @returns VBox error code.
*/
static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisPromiscReq pQueue=%p fPromisc=%d\n", pQueue, fPromisc));
t_uscalar_t Cmd;
size_t cbReq = 0;
if (fPromisc)
{
Cmd = DL_PROMISCON_REQ;
cbReq = DL_PROMISCON_REQ_SIZE;
}
else
{
Cmd = DL_PROMISCOFF_REQ;
cbReq = DL_PROMISCOFF_REQ_SIZE;
}
mblk_t *pPromiscPhysMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
if (RT_UNLIKELY(!pPromiscPhysMsg))
return VERR_NO_MEMORY;
mblk_t *pPromiscSapMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
if (RT_UNLIKELY(!pPromiscSapMsg))
{
freemsg(pPromiscPhysMsg);
return VERR_NO_MEMORY;
}
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
pStream->UnAckPrim = Cmd;
if (fPromisc)
{
((dl_promiscon_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
((dl_promiscon_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
}
else
{
((dl_promiscoff_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
((dl_promiscoff_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
}
qreply(pQueue, pPromiscPhysMsg);
qreply(pQueue, pPromiscSapMsg);
return VINF_SUCCESS;
}
/**
* Send a fake physical address request downstream.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
* @param pMsg Pointer to the request message.
*/
static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisPhysAddrReq pQueue=%p\n", pQueue));
t_uscalar_t Cmd = DL_PHYS_ADDR_REQ;
size_t cbReq = DL_PHYS_ADDR_REQ_SIZE;
mblk_t *pPhysAddrMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
if (RT_UNLIKELY(!pPhysAddrMsg))
return VERR_NO_MEMORY;
vboxnetflt_stream_t *pStream = pQueue->q_ptr;
pStream->UnAckPrim = Cmd;
dl_phys_addr_req_t *pPhysAddrReq = (dl_phys_addr_req_t *)pPhysAddrMsg->b_rptr;
pPhysAddrReq->dl_addr_type = DL_CURR_PHYS_ADDR;
qreply(pQueue, pPhysAddrMsg);
return VERR_GENERAL_FAILURE;
}
/**
* Cache the MAC address into the VirtualBox instance given a physical
* address acknowledgement message.
*
* @param pThis The instance.
* @param pMsg Pointer to the physical address acknowledgement message.
*/
static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr pThis=%p pMsg=%p\n", pThis, pMsg));
AssertCompile(sizeof(RTMAC) == ETHERADDRL);
dl_phys_addr_ack_t *pPhysAddrAck = (dl_phys_addr_ack_t *)pMsg->b_rptr;
if (pPhysAddrAck->dl_addr_length == sizeof(pThis->u.s.Mac))
{
bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac));
LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n", sizeof(pThis->u.s.Mac),
&pThis->u.s.Mac));
}
}
/**
* Opens the required device and returns the vnode_t associated with it.
* We require this for the funny attach/detach routine.
*
* @returns VBox status code.
* @param pszDev The device path.
* @param ppVNode Where to store the vnode_t pointer associated with the opened device.
* @param ppVNodeHeld Where to store the vnode_t required during closing of the device.
* @param ppUser Open handle required while closing the device.
*/
static int vboxNetFltSolarisOpenDev(char *pszDev, vnode_t **ppVNode, vnode_t **ppVNodeHeld, TIUSER **ppUser)
{
int rc;
vnode_t *pVNodeHeld = NULL;
rc = lookupname(pszDev, UIO_SYSSPACE, FOLLOW, NULLVPP, &pVNodeHeld);
if (!rc)
{
TIUSER *pUser;
rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred);
if (!rc)
{
*ppVNode = pUser->fp->f_vnode;
*ppVNodeHeld = pVNodeHeld;
*ppUser = pUser;
return VINF_SUCCESS;
}
VN_RELE(pVNodeHeld);
}
return VERR_PATH_NOT_FOUND;
}
/**
* Close the device opened using vboxNetFltSolarisOpenDev.
*
* @param pVNodeHeld Pointer to the held vnode of the device.
* @param pUser Pointer to the file handle.
*/
static void vboxNetFltSolarisCloseDev(vnode_t *pVNodeHeld, TIUSER *pUser)
{
t_kclose(pUser, 0);
VN_RELE(pVNodeHeld);
}
/**
* Get the logical interface flags from the stream.
*
* @returns VBox status code.
* @param hDevice Layered device handle.
* @param pInterface Pointer to the interface.
*/
static int vboxNetFltSolarisGetIfFlags(ldi_handle_t hDevice, struct lifreq *pInterface)
{
struct strioctl IOCReq;
int rc;
int ret;
IOCReq.ic_cmd = SIOCGLIFFLAGS;
IOCReq.ic_timout = 40;
IOCReq.ic_len = sizeof(struct lifreq);
IOCReq.ic_dp = (caddr_t)pInterface;
rc = ldi_ioctl(hDevice, I_STR, (intptr_t)&IOCReq, FKIOCTL, kcred, &ret);
if (!rc)
return VINF_SUCCESS;
return RTErrConvertFromErrno(rc);
}
/**
* Sets the multiplexor ID from the interface.
*
* @returns VBox status code.
* @param pVNode Pointer to the device vnode.
* @param pInterface Pointer to the interface.
*/
static int vboxNetFltSolarisSetMuxId(vnode_t *pVNode, struct lifreq *pInterface)
{
struct strioctl IOCReq;
int rc;
int ret;
IOCReq.ic_cmd = SIOCSLIFMUXID;
IOCReq.ic_timout = 40;
IOCReq.ic_len = sizeof(struct lifreq);
IOCReq.ic_dp = (caddr_t)pInterface;
rc = strioctl(pVNode, I_STR, (intptr_t)&IOCReq, 0, K_TO_K, kcred, &ret);
if (!rc)
return VINF_SUCCESS;
return RTErrConvertFromErrno(rc);
}
/**
* Get the multiplexor file descriptor of the lower stream.
*
* @returns VBox status code.
* @param MuxId The multiplexor ID.
* @param pFd Where to store the lower stream file descriptor.
*/
static int vboxNetFltSolarisMuxIdToFd(vnode_t *pVNode, int MuxId, int *pFd)
{
int ret;
int rc = strioctl(pVNode, _I_MUXID2FD, (intptr_t)MuxId, 0, K_TO_K, kcred, &ret);
if (!rc)
{
*pFd = ret;
return VINF_SUCCESS;
}
return RTErrConvertFromErrno(rc);
}
/**
* Relinks the lower and the upper stream.
*
* @returns VBox status code.
* @param pVNode Pointer to the device vnode.
* @param pInterface Pointer to the interface.
* @param IpMuxFd The IP multiplexor ID.
* @param ArpMuxFd The ARP multiplexor ID.
*/
static int vboxNetFltSolarisRelink(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelink: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode,
pInterface, IpMuxFd, ArpMuxFd));
int NewIpMuxId;
int NewArpMuxId;
int rc = strioctl(pVNode, I_PLINK, (intptr_t)IpMuxFd, 0, K_TO_K, kcred, &NewIpMuxId);
int rc2 = strioctl(pVNode, I_PLINK, (intptr_t)ArpMuxFd, 0, K_TO_K, kcred, &NewArpMuxId);
if ( !rc
&& !rc2)
{
pInterface->lifr_ip_muxid = NewIpMuxId;
pInterface->lifr_arp_muxid = NewArpMuxId;
rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
LogRel((DEVICE_NAME ":vboxNetFltSolarisRelink: failed to set new Mux Id.\n"));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisRelink: failed to link.\n"));
return VERR_GENERAL_FAILURE;
}
/**
* Dynamically find the position on the host stack where to attach/detach ourselves.
*
* @returns VBox status code.
* @param pVNode Pointer to the lower stream vnode.
* @param pModPos Where to store the module position.
*/
static int vboxNetFltSolarisDetermineModPos(bool fAttach, vnode_t *pVNode, int *pModPos)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos));
int cMod;
int rc = strioctl(pVNode, I_LIST, (intptr_t)NULL, 0, K_TO_K, kcred, &cMod);
if (!rc)
{
if (cMod < 1)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: too few modules on host interface. cMod=%d\n"));
return VERR_OUT_OF_RANGE;
}
/*
* While attaching we make sure we are at the bottom most of the stack, excepting
* the host driver.
*/
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: cMod=%d\n", cMod));
if (fAttach)
{
*pModPos = cMod - 1;
return VINF_SUCCESS;
}
/*
* Detaching is a bit more complicated; since user could have altered the stack positions
* we take the safe approach by finding our position.
*/
struct str_list StrList;
StrList.sl_nmods = cMod;
StrList.sl_modlist = RTMemAllocZ(cMod * sizeof(struct str_list));
if (RT_UNLIKELY(!StrList.sl_modlist))
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to alloc memory for StrList.\n"));
return VERR_NO_MEMORY;
}
/*
* Get the list of all modules on the stack.
*/
int ret;
rc = strioctl(pVNode, I_LIST, (intptr_t)&StrList, 0, K_TO_K, kcred, &ret);
if (!rc)
{
/*
* Find our filter.
*/
for (int i = 0; i < StrList.sl_nmods; i++)
{
if (!strcmp(DEVICE_NAME, StrList.sl_modlist[i].l_name))
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
*pModPos = i;
return VINF_SUCCESS;
}
}
LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n"));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n"));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc));
return VERR_GENERAL_FAILURE;
}
/**
* Dynamically attaches this streams module on to the host stack.
* As a side-effect, this streams also gets opened/closed during
* the actual injection/ejection phase.
*
* @returns VBox status code.
* @param pThis The instance.
* @param fAttach Is this an attach or detach.
*/
static int vboxNetFltSolarisModSetup(PVBOXNETFLTINS pThis, bool fAttach)
{
LogFlow(("vboxNetFltSolarisModSetup: pThis=%p (%s) fAttach=%s\n", pThis, pThis->szName, fAttach ? "true" : "false"));
/*
* Statuatory Warning: Hackish code ahead.
*/
if (strlen(pThis->szName) > VBOXNETFLT_IFNAME_LEN - 1)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: interface name too long %s\n", pThis->szName));
return VERR_INTNET_FLT_IF_NOT_FOUND;
}
char *pszModName = DEVICE_NAME;
struct lifreq Interface;
bzero(&Interface, sizeof(Interface));
Interface.lifr_addr.ss_family = AF_INET;
strncpy(Interface.lifr_name, pThis->szName, sizeof(Interface.lifr_name));
struct strmodconf StrMod;
StrMod.mod_name = pszModName;
StrMod.pos = -1; /* this is filled in later. */
struct strmodconf ArpStrMod;
bcopy(&StrMod, &ArpStrMod, sizeof(StrMod));
int rc;
int rc2;
int ret;
ldi_ident_t IPDevId = ldi_ident_from_anon();
ldi_ident_t ARPDevId = ldi_ident_from_anon();
ldi_handle_t IPDevHandle;
ldi_handle_t UDPDevHandle;
ldi_handle_t ARPDevHandle;
/*
* Open the IP and ARP streams as layered devices.
*/
rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &IPDevHandle, IPDevId);
ldi_ident_release(IPDevId);
if (rc)
{
LogRel((DEVICE_NAME ":failed to open the IP stream on '%s'.\n", pThis->szName));
return VERR_INTNET_FLT_IF_FAILED;
}
rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ARPDevHandle, ARPDevId);
ldi_ident_release(ARPDevId);
if (rc)
{
LogRel((DEVICE_NAME ":failed to open the ARP stream on '%s'.\n", pThis->szName));
ldi_close(IPDevHandle, FREAD | FWRITE, kcred);
return VERR_INTNET_FLT_IF_FAILED;
}
/*
* Obtain the interface flags from IP.
*/
rc = vboxNetFltSolarisGetIfFlags(IPDevHandle, &Interface);
if (RT_SUCCESS(rc))
{
/*
* Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
* things that are not possible from the layered interface.
*/
vnode_t *pVNodeUDP = NULL;
vnode_t *pVNodeUDPHeld = NULL;
TIUSER *pUserUDP = NULL;
rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pVNodeUDP, &pVNodeUDPHeld, &pUserUDP);
if (RT_SUCCESS(rc))
{
/*
* Get the multiplexor IDs.
*/
rc = ldi_ioctl(IPDevHandle, SIOCGLIFMUXID, (intptr_t)&Interface, FKIOCTL, kcred, &ret);
if (!rc)
{
/*
* Get the multiplex file descriptor to the lower streams. Generally this is lost
* once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
*/
int IpMuxFd;
int ArpMuxFd;
rc = vboxNetFltSolarisMuxIdToFd(pVNodeUDP, Interface.lifr_ip_muxid, &IpMuxFd);
rc2 = vboxNetFltSolarisMuxIdToFd(pVNodeUDP, Interface.lifr_arp_muxid, &ArpMuxFd);
if ( RT_SUCCESS(rc)
&& RT_SUCCESS(rc2))
{
/*
* We need to I_PUNLINK on these multiplexor IDs before we can start
* operating on the lower stream as insertions are direct operations on the lower stream.
*/
int ret;
rc = strioctl(pVNodeUDP, I_PUNLINK, (intptr_t)Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
rc2 = strioctl(pVNodeUDP, I_PUNLINK, (intptr_t)Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret);
if ( !rc
&& !rc2)
{
/*
* Obtain the vnode from the useless userland file descriptor.
*/
file_t *pIpFile = getf(IpMuxFd);
file_t *pArpFile = getf(ArpMuxFd);
if ( pIpFile
&& pArpFile
&& pArpFile->f_vnode
&& pIpFile->f_vnode)
{
vnode_t *pVNodeIp = pIpFile->f_vnode;
vnode_t *pVNodeArp = pArpFile->f_vnode;
/*
* Find the position on the host stack for attaching/detaching ourselves.
*/
rc = vboxNetFltSolarisDetermineModPos(fAttach, pVNodeIp, &StrMod.pos);
rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pVNodeArp, &ArpStrMod.pos);
if ( RT_SUCCESS(rc)
&& RT_SUCCESS(rc2))
{
/*
* Set global data which will be grabbed by ModOpen.
* There is a known (though very unlikely) race here because
* of the inability to pass user data while inserting.
*/
g_VBoxNetFltSolarisState.pCurInstance = pThis;
g_VBoxNetFltSolarisState.CurType = kIpStream;
/*
* Inject/Eject from the host IP stack.
*/
rc = strioctl(pVNodeIp, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
kcred, &ret);
if (!rc)
{
/*
* Inject/Eject from the host ARP stack.
*/
g_VBoxNetFltSolarisState.CurType = kArpStream;
rc = strioctl(pVNodeArp, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K,
kcred, &ret);
if (!rc)
{
g_VBoxNetFltSolarisState.pCurInstance = NULL;
g_VBoxNetFltSolarisState.CurType = kUndefined;
/*
* Our job's not yet over; we need to relink the upper and lower streams
* otherwise we've pretty much screwed up the host interface.
*/
rc = vboxNetFltSolarisRelink(pVNodeUDP, &Interface, IpMuxFd, ArpMuxFd);
if (RT_SUCCESS(rc))
{
/*
* Close the devices ONLY during the return from function case; otherwise
* we end up close twice which is an instant kernel panic.
*/
vboxNetFltSolarisCloseDev(pVNodeUDPHeld, pUserUDP);
ldi_close(ARPDevHandle, FREAD | FWRITE, kcred);
ldi_close(IPDevHandle, FREAD | FWRITE, kcred);
LogFlow((DEVICE_NAME ":vboxNetFltSolarisModSetup: Success! %s %s@(Ip:%d Arp:%d) "
"%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
StrMod.pos, ArpStrMod.pos, fAttach ? "to" : "from", pThis->szName));
return VINF_SUCCESS;
}
else
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: Relinking failed. Mode=%s rc=%d.\n",
fAttach ? "inject" : "eject", rc));
}
/*
* Try failing gracefully during attach.
*/
if (fAttach)
strioctl(pVNodeIp, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
}
else
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to %s the ARP stack. rc=%d\n",
fAttach ? "inject into" : "eject from", rc));
}
if (fAttach)
strioctl(pVNodeIp, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
vboxNetFltSolarisRelink(pVNodeUDP, &Interface, IpMuxFd, ArpMuxFd);
}
else
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to %s the IP stack. rc=%d\n",
fAttach ? "inject into" : "eject from", rc));
}
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to find position. rc=%d rc2=%d\n", rc, rc2));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to get vnode from MuxFd.\n"));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to get MuxFd from MuxId. rc=%d rc2=%d\n"));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to get Mux Ids. rc=%d\n", rc));
vboxNetFltSolarisCloseDev(pVNodeUDPHeld, pUserUDP);
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: failed to open UDP. rc=%d\n", rc));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisModSetup: invalid interface '%s'.\n", pThis->szName));
ldi_close(ARPDevHandle, FREAD | FWRITE, kcred);
ldi_close(IPDevHandle, FREAD | FWRITE, kcred);
return VERR_INTNET_FLT_IF_FAILED;
}
/**
* Wrapper for attaching ourselves to the interface.
*
* @returns VBox status code.
* @param pThis The instance.
* @remarks Owns the globals mutex, so re-requesting it anytime during this phase
* would panic the system e.g. in vboxNetFltSolarisFindInstance).
*/
static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis)
{
int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
AssertRC(rc);
rc = vboxNetFltSolarisModSetup(pThis, true);
RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
return rc;
}
/**
* Wrapper for detaching ourselves from the interface.
*
* @returns VBox status code.
* @param pThis The instance.
* @remarks Owns the globals mutex, so re-requesting it anytime during this phase
* would panic the system (e.g. in vboxNetFltSolarisFindInstance).
*/
static int vboxNetFltSolarisDetachFromInterface(PVBOXNETFLTINS pThis)
{
int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
AssertRC(rc);
rc = vboxNetFltSolarisModSetup(pThis, false);
RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
return rc;
}
/**
* Create a solaris message block from the SG list.
*
* @returns Solaris message block.
* @param pThis The instance.
* @param pSG Pointer to the scatter-gather list.
*/
static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n"));
mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_MED);
if (RT_UNLIKELY(!pMsg))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
return NULL;
}
/*
* Single buffer copy. Maybe later explore the
* need/possibility for using a mblk_t chain rather.
*/
for (unsigned i = 0; i < pSG->cSegsUsed; i++)
{
if (pSG->aSegs[i].pv)
{
bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
pMsg->b_wptr += pSG->aSegs[i].cb;
}
}
DB_TYPE(pMsg) = M_DATA;
return pMsg;
}
/**
* Calculate the number of segments required for this message block.
*
* @returns Number of segments.
* @param pThis The instance
* @param pMsg Pointer to the data message.
*/
static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
{
unsigned cSegs = 0;
for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
if (MBLKL(pCur))
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
if (msgdsize(pMsg) < 60)
cSegs++;
#endif
NOREF(pThis);
return RT_MAX(cSegs, 1);
}
/**
* Initializes an SG list from the given message block.
*
* @returns VBox status code.
* @param pThis The instance.
* @param pMsg Pointer to the data message.
The caller must ensure it's not a control message block.
* @param pSG Pointer to the SG.
* @param cSegs Number of segments in the SG.
* This should match the number in the message block exactly!
* @param fSrc The source of the message.
*/
static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
pSG->pvOwnerData = NULL;
pSG->pvUserData = NULL;
pSG->pvUserData2 = NULL;
pSG->cUsers = 1;
pSG->cbTotal = 0;
pSG->fFlags = INTNETSG_FLAGS_TEMP;
pSG->cSegsAlloc = cSegs;
/*
* Convert the message block to segments.
*/
mblk_t *pCur = pMsg;
unsigned iSeg = 0;
while (pCur)
{
size_t cbSeg = MBLKL(pCur);
if (cbSeg)
{
void *pvSeg = pCur->b_rptr;
pSG->aSegs[iSeg].pv = pvSeg;
pSG->aSegs[iSeg].cb = cbSeg;
pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
pSG->cbTotal += cbSeg;
iSeg++;
}
pCur = pCur->b_cont;
}
pSG->cSegsUsed = iSeg;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
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
LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
return VINF_SUCCESS;
}
/**
* Converts raw mode M_DATA messages to M_PROTO DL_UNITDATA_IND format.
*
* @returns VBox status code.
* @param pMsg Pointer to the raw message.
* @param pDlpiMsg Where to store the M_PROTO message.
*
* @remarks The original raw message would be no longer valid and will be
* linked as part of the new DLPI message. Callers must take care
* not to use the raw message if this routine is successful.
*/
static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData pMsg=%p\n", pMsg));
if (DB_TYPE(pMsg) != M_DATA)
return VERR_NO_MEMORY;
size_t cbMsg = sizeof(dl_unitdata_ind_t) + 2 * sizeof(vboxnetflt_dladdr_t);
mblk_t *pDlpiMsg = allocb(cbMsg, BPRI_MED);
if (RT_UNLIKELY(!pDlpiMsg))
return VERR_NO_MEMORY;
DB_TYPE(pDlpiMsg) = M_PROTO;
dl_unitdata_ind_t *pDlpiData = (dl_unitdata_ind_t *)pDlpiMsg->b_rptr;
pDlpiData->dl_primitive = DL_UNITDATA_IND;
pDlpiData->dl_dest_addr_length = VBOXNETFLT_DLADDRL;
pDlpiData->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
pDlpiData->dl_src_addr_length = VBOXNETFLT_DLADDRL;
pDlpiData->dl_src_addr_offset = VBOXNETFLT_DLADDRL + sizeof(dl_unitdata_ind_t);
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset);
pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
bcopy(&pEthHdr->DstMac, &pDlAddr->Mac, sizeof(RTMAC));
pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_src_addr_offset);
pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
bcopy(&pEthHdr->SrcMac, &pDlAddr->Mac, sizeof(RTMAC));
pDlpiMsg->b_wptr = pDlpiMsg->b_rptr + cbMsg;
/* Make the message point to the protocol header */
pMsg->b_rptr += sizeof(RTNETETHERHDR);
pDlpiMsg->b_cont = pMsg;
*ppDlpiMsg = pDlpiMsg;
return VINF_SUCCESS;
}
/**
* Converts DLPI M_PROTO messages to the raw mode M_DATA format.
*
* @returns VBox status code.
* @param pMsg Pointer to the M_PROTO message.
* @param ppRawMsg Where to store the converted message.
*
* @remarks If successful, the original pMsg is no longer valid, it will be deleted.
* Callers must take care not to continue to use pMsg after a successful
* call to this conversion routine.
*/
static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw pMsg=%p\n", pMsg));
if ( !pMsg->b_cont
|| DB_TYPE(pMsg) != M_PROTO)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw invalid input message.\n"));
return VERR_NET_PROTOCOL_ERROR;
}
/*
* Upstream consumers send/receive packets in the fast path mode.
* We of course need to convert them into raw ethernet frames.
*/
RTNETETHERHDR EthHdr;
union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
switch (pPrim->dl_primitive)
{
case DL_UNITDATA_IND:
{
/*
* Receive side.
*/
dl_unitdata_ind_t *pDlpiMsg = (dl_unitdata_ind_t *)pMsg->b_rptr;
bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
bcopy(pMsg->b_rptr + pDlpiMsg->dl_src_addr_offset, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
break;
}
case DL_UNITDATA_REQ:
{
/*
* Send side.
*/
dl_unitdata_req_t *pDlpiMsg = (dl_unitdata_req_t *)pMsg->b_rptr;
bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
bcopy(&pThis->u.s.Mac, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
break;
}
default:
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw Unknown M_PROTO. This shouldn't be happening!!"));
return VERR_NET_PROTOCOL_ERROR;
}
}
/*
* Let us just link it as a mblk_t chain rather than re-copy the entire message.
* The vboxNetFltSolarisMBlkToSG function will handle chained mblk_t's.
*/
size_t cbLen = sizeof(EthHdr);
mblk_t *pEtherMsg = allocb(cbLen, BPRI_MED);
if (RT_UNLIKELY(!pEtherMsg))
return VERR_NO_MEMORY;
DB_TYPE(pEtherMsg) = M_DATA;
bcopy(&EthHdr, pEtherMsg->b_wptr, sizeof(EthHdr));
pEtherMsg->b_wptr += cbLen;
pEtherMsg->b_cont = pMsg->b_cont;
/*
* Change the chained blocks to type M_DATA.
*/
for (mblk_t *pTmp = pEtherMsg->b_cont; pTmp; pTmp = pTmp->b_cont)
DB_TYPE(pTmp) = M_DATA;
pMsg->b_cont = NULL;
freemsg(pMsg);
*ppRawMsg = pEtherMsg;
return VINF_SUCCESS;
}
/**
* Worker for routing messages from the wire or from the host.
*
* @returns VBox status code.
* @param pThis The instance.
* @param pStream Pointer to the stream.
* @param pQueue Pointer to the queue.
* @param pOrigMsg Pointer to the message.
*/
static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pOrigMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pOrigMsg=%p\n", pThis, pOrigMsg));
AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR));
Assert(pStream->Type == kIpStream);
/*
* Due to the asynchronous nature of streams we may get messages before the
* raw mode is turned on, in this case just pass through.
*/
if (!pStream->fRawMode)
{
LogFlow((DEVICE_NAME ":IP stream not yet in raw mode. Passing through packet.\n"));
putnext(pQueue, pOrigMsg);
return VINF_SUCCESS;
}
/*
* Make a copy as we will alter pMsg.
*/
mblk_t *pMsg = copymsg(pOrigMsg);
/*
* Sort out M_PROTO and M_DATA.
*/
bool fOriginalIsRaw = true;
if (DB_TYPE(pMsg) == M_PROTO)
{
fOriginalIsRaw = false;
mblk_t *pRawMsg;
int rc = vboxNetFltSolarisUnitDataToRaw(pThis, pMsg, &pRawMsg);
if (RT_SUCCESS(rc))
pMsg = pRawMsg;
else
{
freemsg(pMsg);
LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv invalid message!\n"));
return VERR_GENERAL_FAILURE;
}
}
/*
* Figure out the source of the packet based on the source Mac address.
*/
/** @todo Is there a more fool-proof way to determine if the packet was indeed sent from the host?? */
uint32_t fSrc = INTNETTRUNKDIR_WIRE;
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
if (vboxNetFltPortOsIsHostMac(pThis, &pEthHdr->SrcMac))
fSrc = INTNETTRUNKDIR_HOST;
bool fChecksumAdjusted = false;
#if 1
if (fSrc & INTNETTRUNKDIR_HOST)
{
mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg);
if (pCorrectedMsg)
{
fChecksumAdjusted = true;
pMsg = pCorrectedMsg;
}
}
#endif
/*
* Route all received packets into the internal network.
*/
unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg);
PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
if (VBOX_FAILURE(rc))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc));
return rc;
}
bool fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
/*
* Drop messages consumed by the internal network.
*/
if (fDropIt)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRecv Packet consumed by internal network.\n"));
freemsg(pOrigMsg);
freemsg(pMsg);
}
else
{
/*
* Packets from the host, push them up in raw mode.
* Packets from the wire, we must push them in their original form
* as upstream consumers expect this format.
*/
if (fSrc & INTNETTRUNKDIR_HOST)
{
/* Raw packets with correct checksums, pass-through the original */
if ( fOriginalIsRaw
&& !fChecksumAdjusted)
{
putnext(pQueue, pOrigMsg);
}
else /* For M_PROTO packets or checksum corrected raw packets, pass-through the raw */
{
putnext(pQueue, pMsg);
pMsg = pOrigMsg; /* for the freemsg that follows */
}
}
else /* INTNETTRUNKDIR_WIRE */
{
if (fOriginalIsRaw)
pOrigMsg->b_rptr += sizeof(RTNETETHERHDR);
putnext(pQueue, pOrigMsg);
}
freemsg(pMsg);
}
return VINF_SUCCESS;
}
/**
* Find the PVBOXNETFLTINS associated with a stream.
*
* @returns PVBOXNETFLTINS instance, or NULL if there's none.
* @param pStream Pointer to the stream to search for.
*/
static PVBOXNETFLTINS vboxNetFltSolarisFindInstance(vboxnetflt_stream_t *pStream)
{
if (!pStream)
return NULL;
vboxnetflt_stream_t *pCur = g_VBoxNetFltSolarisState.pOpenedStreams;
for (; pCur; pCur = pCur->pNext)
if (pCur == pStream)
return pCur->pThis;
return NULL;
}
/**
* Finalize the message to be fed into the internal network.
* Verifies and tries to fix checksums for TCP, UDP and IP.
*
* @returns Corrected message or NULL if no change was required.
* @param pMsg Pointer to the message block.
* This must not be DLPI linked messages, must be M_DATA.
*
* @remarks If this function returns a checksum adjusted message, the
* passed in input message has been freed and should not be
* referenced anymore by the caller.
*/
static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisFixChecksums pMsg=%p\n"));
Assert(DB_TYPE(pMsg) == M_DATA);
if (MBLKL(pMsg) < sizeof(RTNETETHERHDR))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums Packet shorter than ethernet header size!\n"));
return NULL;
}
PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
{
/*
* Check if we have a complete packet or being fed a chain.
*/
size_t cbIpPacket = 0;
mblk_t *pFullMsg = NULL;
if (pMsg->b_cont)
{
LogFlow((DEVICE_NAME ":Chained mblk_t.\n"));
/*
* Handle chain by making a packet copy to verify if the IP checksum is correct.
* Contributions to calculating IP checksums from a chained message block with
* odd/non-pulled up sizes are welcome.
*/
size_t cbFullMsg = msgdsize(pMsg);
mblk_t *pFullMsg = allocb(cbFullMsg, BPRI_MED);
LogFlow((DEVICE_NAME ":msgdsize returns %d\n", cbFullMsg));
if (RT_UNLIKELY(!pFullMsg))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg));
return false;
}
for (mblk_t *pTmp = pMsg; pTmp; pTmp = pTmp->b_cont)
{
if (DB_TYPE(pTmp) == M_DATA)
{
bcopy(pTmp->b_rptr, pFullMsg->b_wptr, MBLKL(pTmp));
pFullMsg->b_wptr += MBLKL(pTmp);
}
else
LogFlow((DEVICE_NAME ":Not M_DATA.. this is really bad.\n"));
}
DB_TYPE(pFullMsg) = M_DATA;
pEthHdr = (PRTNETETHERHDR)pFullMsg->b_rptr;
cbIpPacket = MBLKL(pFullMsg) - sizeof(RTNETETHERHDR);
}
else
cbIpPacket = MBLKL(pMsg) - sizeof(RTNETETHERHDR);
/*
* Check if the IP checksum is valid.
*/
PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
if (!RTNetIPv4IsHdrValid(pIpHdr, cbIpPacket, cbIpPacket))
{
LogFlow((DEVICE_NAME ":Invalid IP checksum detected. Trying to fix...\n"));
/*
* Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
*/
if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
{
size_t cbTcpPacket = cbIpPacket - (pIpHdr->ip_hl << 2);
PRTNETTCP pTcpHdr = (PRTNETTCP)(pIpHdr + 1);
if (!RTNetIPv4IsTCPValid(pIpHdr, pTcpHdr, cbTcpPacket, NULL, cbTcpPacket))
{
RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL);
LogFlow((DEVICE_NAME ":fixed TCP checkum.\n"));
}
else
LogFlow((DEVICE_NAME ":valid TCP checksum invalid IP checksum...\n"));
}
else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
{
size_t cbUdpPacket = cbIpPacket - (pIpHdr->ip_hl << 2);
PRTNETUDP pUdpHdr = (PRTNETUDP)(pIpHdr + 1);
RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
LogFlow((DEVICE_NAME ":fixed UDP checksum.\n"));
}
else
{
LogFlow((DEVICE_NAME ":Non TCP/UDP protocol with invalid IP header!\n"));
}
RTNetIPv4HdrChecksum(pIpHdr);
LogFlow((DEVICE_NAME ":fixed IP checkum.\n"));
/*
* If we made a copy and the checksum is corrected on the copy,
* free the original, return the checksum fixed copy.
*/
if (pFullMsg)
{
freemsg(pMsg);
return pFullMsg;
}
return pMsg;
}
/*
* If we made a copy and the checksum is NOT corrected, free the copy,
* and return NULL.
*/
if (pFullMsg)
freemsg(pFullMsg);
}
return NULL;
}
/**
* Simple packet dump, used for internal debugging.
*
* @param pMsg Pointer to the message to analyze and dump.
*/
static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n"));
PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
{
PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
size_t cbLen = MBLKL(pMsg) - sizeof(*pEthHdr);
if (RTNetIPv4IsHdrValid(pIpHdr, cbLen, cbLen))
{
uint8_t *pb = pMsg->b_rptr;
if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
LogFlow((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
LogFlow((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
LogFlow((DEVICE_NAME ":UDP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
}
else
{
LogFlow((DEVICE_NAME ":Invalid IP header.\n"));
return;
}
}
else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
{
PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
LogFlow((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
}
else
{
LogFlow((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
&pEthHdr->SrcMac));
/* LogFlow((DEVICE_NAME ":%.*Vhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
}
}
/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis)
{
vboxnetflt_stream_t *pStream = pThis->u.s.pvStream;
return pStream->fPromisc;
}
void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac)
{
LogFlow((DEVICE_NAME ":vboxNetFltPortOsGetMacAddress pThis=%p\n", pThis));
RTMAC EmptyMac;
bzero(&EmptyMac, sizeof(EmptyMac));
if (!bcmp(&pThis->u.s.Mac, &EmptyMac, sizeof(EmptyMac))) /* This should never happen... */
{
/* Bummer, Mac address is not copied yet. */
LogRel((DEVICE_NAME ":vboxNetFltPortOsGetMacAddress: Mac address not cached yet!\n"));
return;
}
*pMac = pThis->u.s.Mac;
}
bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
{
/*
* MAC address change acknowledgements are intercepted on the read side
* hence theoritically we are always update to date with any 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];
}
void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
{
LogFlow((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
/*
* Enable/disable promiscuous mode.
*/
vboxnetflt_stream_t *pStream = pThis->u.s.pvStream;
Assert(pStream->Type == kIpStream);
int rc = vboxNetFltSolarisPromiscReq(pStream->pReadQueue, fActive);
if (VBOX_FAILURE(rc))
LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive failed to request promiscuous mode! rc=%d\n", rc));
}
int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
{
LogFlow((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n"));
vboxNetFltSolarisDetachFromInterface(pThis);
}
int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis)
{
LogFlow((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n"));
return vboxNetFltSolarisAttachToInterface(pThis);
}
int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
{
/*
* Init. the solaris specific data.
*/
pThis->u.s.pvStream = NULL;
pThis->u.s.pvArpStream = NULL;
bzero(&pThis->u.s.Mac, sizeof(pThis->u.s.Mac));
return VINF_SUCCESS;
}
bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
{
/*
* We don't support interface rediscovery on Solaris hosts because the
* filter is very tightly bound to the stream.
*/
return false;
}
int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
{
LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst));
vboxnetflt_stream_t *pStream = pThis->u.s.pvStream;
queue_t *pReadQueue = pStream->pReadQueue;
queue_t *pWriteQueue = WR(pReadQueue);
/*
* Create a message block and send it down the wire (downstream).
*/
if (fDst & INTNETTRUNKDIR_WIRE)
{
mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
if (RT_UNLIKELY(!pMsg))
{
LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
return VERR_NO_MEMORY;
}
LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n", pMsg));
putnext(pWriteQueue, pMsg);
}
/*
* Create a message block and send it up the host stack (upstream).
*/
if (fDst & INTNETTRUNKDIR_HOST)
{
mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
if (RT_UNLIKELY(!pMsg))
{
LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
return VERR_NO_MEMORY;
}
PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
bool fArp = (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP));
/*
* Construct a DL_UNITDATA_IND style message.
*/
mblk_t *pDlpiMsg;
int rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg);
if (VBOX_FAILURE(rc))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed! rc=%d\n", rc));
freemsg(pMsg);
return VERR_NO_MEMORY;
}
pMsg = pDlpiMsg;
if (fArp)
{
LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n"));
/*
* Send message up ARP stream.
*/
vboxnetflt_stream_t *pArpStream = pThis->u.s.pvArpStream;
queue_t *pArpReadQueue = pArpStream->pReadQueue;
putnext(pArpReadQueue, pMsg);
}
else
{
LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST\n"));
/*
* Send messages up IP stream.
*/
putnext(pReadQueue, pMsg);
}
}
return VINF_SUCCESS;
}