VBoxNetFlt-solaris.c revision 5cc593e7c1fa3f953076eb49aa8af00c9a56994e
/* $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 <iprt/initterm.h>
#include <iprt/spinlock.h>
#include <sys/pathname.h>
#include <sys/ethernet.h>
/*
* Experimental: Using netinfo interfaces and queuing out packets.
* This is for playing better with IPFilter.
*/
#endif
// #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 *
*******************************************************************************/
#define VBOXSOLQUOTE2(x) #x
#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x)
/** The module name. */
#define DEVICE_NAME "vboxflt"
/** The module descriptions as seen in 'modinfo'. */
#define DEVICE_DESC_DRV "VirtualBox NetDrv"
#define DEVICE_DESC_MOD "VirtualBox NetMod"
/** @todo Remove the below hackery once done! */
#if defined(DEBUG_ramshankar) && defined(LOG_ENABLED)
#endif
/** Maximum loopback packet queue size per interface */
#define VBOXNETFLT_LOOPBACK_SIZE 32
/*******************************************************************************
* Global Functions *
*******************************************************************************/
/**
* Stream Driver hooks.
*/
static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
/**
* Stream Module hooks.
*/
static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred);
/**
* OS specific hooks invoked from common VBoxNetFlt ring-0.
*/
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Streams: module info.
*/
static struct module_info g_VBoxNetFltSolarisModInfo =
{
0xbad, /* module id */
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 =
{
NULL, /* service */
NULL, /* admin (reserved) */
NULL /* module stats */
};
/**
* Streams: write queue hooks.
*/
static struct qinit g_VBoxNetFltSolarisWriteQ =
{
NULL, /* open */
NULL, /* close */
NULL, /* admin (reserved) */
NULL /* module stats */
};
/**
* Streams: IO stream tab.
*/
static struct streamtab g_VBoxNetFltSolarisStreamTab =
{
NULL, /* muxread init */
NULL /* muxwrite init */
};
/**
*/
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 */
CB_REV /* revision */
};
/**
*/
static struct dev_ops g_VBoxNetFltSolarisDevOps =
{
DEVO_REV, /* driver build revision */
0, /* ref count */
nulldev, /* identify */
nulldev, /* probe */
nodev, /* reset */
(struct bus_ops *)0,
nodev /* power */
};
/**
* modldrv: export driver specifics to kernel
*/
static struct modldrv g_VBoxNetFltSolarisDriver =
{
&mod_driverops, /* extern from kernel */
};
/**
* fmodsw: streams module ops
*/
static struct fmodsw g_VBoxNetFltSolarisModOps =
{
};
/**
* modlstrmod: streams module specifics to kernel
*/
static struct modlstrmod g_VBoxNetFltSolarisModule =
{
&mod_strmodops, /* extern from 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
{
#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t)
/**
* which stream is this?
*/
typedef enum VBOXNETFLTSTREAMTYPE
{
kUndefined = 0,
kIp4Stream = 0x1b,
kIp6Stream = 0xcc,
kArpStream = 0xab,
kPromiscStream = 0xdf
/**
* loopback packet identifier
*/
typedef struct VBOXNETFLTPACKETID
{
struct VBOXNETFLTPACKETID *pNext;
typedef struct VBOXNETFLTPACKETID *PVBOXNETFLTPACKETID;
/**
* vboxnetflt_stream_t: per-stream data (multiple streams per interface)
*/
typedef struct vboxnetflt_stream_t
{
int DevMinor; /* minor device no. (for clone) */
/**
* vboxnetflt_promisc_stream_t: per-interface dedicated stream data
*/
typedef struct vboxnetflt_promisc_stream_t
{
bool fPromisc; /* cached promiscous value */
bool fRawMode; /* whether raw mode request was successful */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */
static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, 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);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Global device info handle. */
/** The (common) global data. */
/** Mutex protecting dynamic binding of the filter. */
/** The list of all opened streams. */
/**
* g_VBoxNetFltInstance is the current PVBOXNETFLTINS 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 volatile g_VBoxNetFltSolarisInstance;
/** GCC C++ hack. */
unsigned __gxx_personality_v0 = 0xdecea5ed;
/**
* Kernel entry points
*/
int _init(void)
{
/*
* Prevent module autounloading.
*/
if (pModCtl)
else
/*
* Initialize IPRT.
*/
if (RT_SUCCESS(rc))
{
/*
* Initialize Solaris specific globals here.
*/
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.
*/
if (RT_SUCCESS(rc))
{
if (!rc)
return rc;
}
else
}
else
RTR0Term();
}
else
return -1;
}
int _fini(void)
{
int rc;
/*
* Undo the work done during start (in reverse order).
*/
if (RT_FAILURE(rc))
{
return EBUSY;
}
{
}
RTR0Term();
return mod_remove(&g_VBoxNetFltSolarisModLinkage);
}
{
return rc;
}
/**
* Attach entry point, to attach a device to the system or resume it.
*
* @param pDip The module structure instance.
*
* @returns corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_ATTACH:
{
if (rc == DDI_SUCCESS)
{
return DDI_SUCCESS;
}
else
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.
*
* @returns corresponding solaris error code.
*/
{
switch (enmCmd)
{
case DDI_DETACH:
{
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,
switch (enmCmd)
{
case DDI_INFO_DEVT2DEVINFO:
{
return DDI_SUCCESS;
}
case DDI_INFO_DEVT2INSTANCE:
{
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)
{
LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev,
fOpenMode, fStreamMode));
/*
* Already open?
*/
{
return ENOENT;
}
/*
* Check for the VirtualBox instance.
*/
if (!pThis)
{
return ENOENT;
}
/*
* Check VirtualBox stream type.
*/
{
return ENOENT;
}
/*
* Get minor number. For clone opens provide a new dev_t.
*/
if (fStreamMode == CLONEOPEN)
{
{
break;
DevMinor++;
}
}
else
{
if (RT_UNLIKELY(!pPromiscStream))
{
return ENOMEM;
}
pPromiscStream->fPromisc = false;
pPromiscStream->fRawMode = false;
pPromiscStream->ModeReqId = 0;
pPromiscStream->cLoopback = 0;
}
else
{
/*
* Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
*/
if (RT_UNLIKELY(!pStream))
{
return ENOMEM;
}
}
/*
* Pick up the current global VBOXNETFLTINS instance as
* the one that we will associate this stream with.
*/
{
case kPromiscStream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvPromiscStream, pStream); break;
default: /* Heh. */
{
break;
}
}
/*
* Link it to the list of streams.
*/
*ppPrevStream = pStream;
/*
* Don't hold the spinlocks across putnext calls as it could
* (and does mostly) re-enter the put procedure on the same thread.
*/
{
/*
* Bind to SAP 0 (DL_ETHER).
* Note: We don't support DL_TPR (token passing ring) SAP as that is unnecessary asynchronous
* work to get DL_INFO_REQ acknowledgements and determine SAP based on the Mac Type etc.
* Besides TPR doesn't really exist anymore practically as far as I know.
*/
{
/*
* Request the physical address (we cache the acknowledgement).
*/
/** @todo take a look at DLPI notifications additionally for these things. */
{
/*
* Ask for DLPI link notifications, don't bother check for errors here.
*/
/*
* Enable raw mode.
*/
if (RT_FAILURE(rc))
}
else
}
else
}
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.
*/
{
/*
* Get instance data.
*/
if (RT_UNLIKELY(!pStream))
{
return ENXIO;
}
{
}
{
/*
* Free-up loopback buffers.
*/
while (pCur)
{
}
pPromiscStream->cLoopback = 0;
}
/*
* Unlink it from the list of streams.
*/
for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
break;
/*
* Delete the stream.
*/
{
default: /* Heh. */
{
break;
}
}
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.
*/
{
if (!pMsg)
return 0;
bool fSendUpstream = true;
/*
* 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
{
{
/*
* Retain the instance if we're filtering regardless of we are active or not
* The reason being even when we are inactive we reference the instance (e.g
* the promiscuous OFF acknowledgement case).
*/
vboxNetFltRetain(pThis, true);
{
case M_DATA:
{
if ( fActive
&& pPromiscStream->fRawMode)
{
fSendUpstream = false;
}
break;
}
case M_PROTO:
case M_PCPROTO:
{
switch (Prim)
{
case DL_NOTIFY_IND:
{
{
LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d got=%d\n",
fSendUpstream = false;
break;
}
switch (pNotifyInd->dl_notification)
{
case DL_NOTE_PHYS_ADDR:
{
break;
{
fSendUpstream = false;
break;
}
fSendUpstream = false;
break;
}
case DL_NOTE_LINK_UP:
{
if (fDisconnected)
{
}
fSendUpstream = false;
break;
}
case DL_NOTE_LINK_DOWN:
{
if (!fDisconnected)
{
}
fSendUpstream = false;
break;
}
}
break;
}
case DL_BIND_ACK:
{
/*
* Swallow our bind request acknowledgement.
*/
fSendUpstream = false;
break;
}
case DL_PHYS_ADDR_ACK:
{
/*
* Swallow our physical address request acknowledgement.
*/
fSendUpstream = false;
break;
}
case DL_OK_ACK:
{
/*
* Swallow our fake promiscous request acknowledgement.
*/
{
pPromiscStream->fPromisc = true;
}
{
pPromiscStream->fPromisc = false;
}
fSendUpstream = false;
break;
}
}
break;
}
case M_IOCACK:
{
/*
*/
{
pPromiscStream->fRawMode = true;
fSendUpstream = false;
}
break;
}
case M_FLUSH:
{
/*
* We must support flushing queues.
*/
break;
}
}
vboxNetFltRelease(pThis, true);
}
else
}
if (fSendUpstream)
{
/*
* Pass foward messages when adjacent module can receive, otherwise queue them.
*/
if (canputnext(pQueue))
else
}
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.
*/
{
if (pMsg)
{
/*
* Pass foward messages when adjacent module can receive, otherwise queue them.
*/
if (canputnext(pQueue))
else
}
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.
*/
{
/*
* Implement just the flow controlled service draining of the queue.
* Nothing else to do here, we handle all the important stuff in the Put procedure.
*/
{
if (canputnext(pQueue))
else
{
break;
}
}
return 0;
}
/**
* Put the stream in raw mode.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
*/
{
if (RT_UNLIKELY(!pRawMsg))
return VERR_NO_MEMORY;
if (!pQueue)
return VERR_INVALID_POINTER;
return VINF_SUCCESS;
}
#if 0
/**
* Put the stream back in fast path mode.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
*/
{
if (RT_UNLIKELY(!pFastMsg))
return VERR_NO_MEMORY;
if (RT_UNLIKELY(!pDataReqMsg))
return VERR_NO_MEMORY;
/*
* Link the data format request message into the header ioctl message.
*/
return VINF_SUCCESS;
}
#endif
/**
* 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.
*/
{
if (fPromisc)
{
}
else
{
}
if (RT_UNLIKELY(!pPromiscPhysMsg))
return VERR_NO_MEMORY;
if (RT_UNLIKELY(!pPromiscSapMsg))
{
return VERR_NO_MEMORY;
}
if (fPromisc)
{
}
else
{
}
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.
*/
{
if (RT_UNLIKELY(!pPhysAddrMsg))
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
/**
* 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.
*/
{
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n", sizeof(pThis->u.s.Mac),
}
else
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL,
}
}
/**
* Prepare DLPI bind request to a SAP.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
* @param SAP The SAP to bind the stream to.
*/
{
if (RT_UNLIKELY(!pBindMsg))
return VERR_NO_MEMORY;
pBindReq->dl_max_conind = 0;
pBindReq->dl_conn_mgmt = 0;
pBindReq->dl_xidtest_flg = 0;
return VINF_SUCCESS;
}
/**
* Prepare DLPI notifications request.
*
* @returns VBox status code.
* @param pQueue Pointer to the queue.
*/
{
if (RT_UNLIKELY(!pNotifyMsg))
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
/**
* Opens the required device and returns the vnode_t associated with it.
*
* @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;
if (!rc)
{
if (!rc)
{
return VINF_SUCCESS;
}
}
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.
*/
{
}
/**
* Get the logical interface flags from the stream.
*
* @returns VBox status code.
* @param hDevice Layered device handle.
* @param pInterface Pointer to the interface.
*/
{
int rc;
int 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.
*/
{
int rc;
int 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.
*/
{
int ret;
if (!rc)
{
return VINF_SUCCESS;
}
return RTErrConvertFromErrno(rc);
}
/**
* Relinks the lower and the upper IPv4 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 vboxNetFltSolarisRelinkIp4(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode,
int NewIpMuxId;
int NewArpMuxId;
if ( !rc
&& !rc2)
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
}
else
return VERR_GENERAL_FAILURE;
}
/**
* Relinks the lower and the upper IPv6 stream.
*
* @returns VBox status code.
* @param pVNode Pointer to the device vnode.
* @param pInterface Pointer to the interface.
* @param Ip6MuxFd The IPv6 multiplexor ID.
*/
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd));
int NewIp6MuxId;
if (!rc)
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
}
else
return VERR_GENERAL_FAILURE;
}
/**
*
* @returns VBox status code.
* @param pVNode Pointer to the lower stream vnode.
* @param pModPos Where to store the module position.
*/
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos));
int 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.
*/
if (fAttach)
{
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.
*/
{
return VERR_NO_MEMORY;
}
/*
* Get the list of all modules on the stack.
*/
int ret;
if (!rc)
{
/*
* Find our filter.
*/
{
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
*pModPos = i;
return VINF_SUCCESS;
}
}
}
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;
}
/**
* Opens up dedicated stream on top of the interface.
* As a side-effect, the stream gets opened during
* the I_PUSH phase.
*
* @param pThis The instance.
*/
{
DevId = ldi_ident_from_anon();
int ret;
/** @todo support DLPI style 2.*/
/*
* Try style-1 open first.
*/
char szDev[128];
if ( rc
{
/*
* Fallback to non-ClearView style-1 open.
*/
}
if (rc)
{
return VERR_INTNET_FLT_IF_FAILED;
}
if (!rc)
{
if (!ret)
{
if (!rc)
return VINF_SUCCESS;
LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to push filter onto host interface '%s'\n", pThis->szName));
}
else
return VINF_SUCCESS;
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to search for filter in interface '%s'.\n", pThis->szName));
return VERR_INTNET_FLT_IF_FAILED;
}
/**
* Closes the interface, thereby closing the dedicated stream.
*
* @param pThis The instance.
*/
{
}
/**
* Dynamically attach under IPv4 and ARP streams on the host stack.
*
* @returns VBox status code.
* @param pThis The instance.
* @param fAttach Is this an attach or detach.
*/
{
/*
* Statuatory Warning: Hackish code ahead.
*/
char *pszModName = DEVICE_NAME;
struct lifreq Ip4Interface;
struct strmodconf StrMod;
struct strmodconf ArpStrMod;
int rc;
int rc2;
int ret;
/*
* Open the IP and ARP streams as layered devices.
*/
if (rc)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName));
return VERR_INTNET_FLT_IF_FAILED;
}
if (rc)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName));
return VERR_INTNET_FLT_IF_FAILED;
}
/*
* Obtain the interface flags from IP.
*/
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.
*/
if (RT_SUCCESS(rc))
{
/*
* Get the multiplexor IDs.
*/
if (!rc)
{
/*
* Get the multiplex file descriptor to the lower streams. Generally this is lost
*/
int Ip4MuxFd;
int 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;
rc2 = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret);
if ( !rc
&& !rc2)
{
/*
* Obtain the vnode from the useless userland file descriptor.
*/
if ( pIpFile
&& pArpFile
{
/*
*/
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.
*/
/*
*/
if (!rc)
{
/*
*/
if (!rc)
{
/*
* 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.
*/
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.
*/
return VINF_SUCCESS;
}
else
{
}
/*
* Try failing gracefully during attach.
*/
if (fAttach)
}
else
{
}
if (fAttach)
}
else
{
}
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc, rc2));
}
else
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get MuxFd from MuxId. rc=%d rc2=%d\n"));
}
else
}
else
}
else
{
/*
* This would happen for interfaces that are not plumbed.
*/
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Warning: seems '%s' is unplumbed.\n", pThis->szName));
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
return rc;
return VERR_INTNET_FLT_IF_FAILED;
}
/**
* Dynamically attach under IPv6 on the host stack.
*
* @returns VBox status code.
* @param pThis The instance.
* @param fAttach Is this an attach or detach.
*/
{
/*
* Statuatory Warning: Hackish code ahead.
*/
char *pszModName = DEVICE_NAME;
struct lifreq Ip6Interface;
struct strmodconf StrMod;
int rc;
int rc2;
int ret;
/*
* Open the IPv6 stream as a layered devices.
*/
if (rc)
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open the IPv6 stream on '%s'.\n", pThis->szName));
return VERR_INTNET_FLT_IF_FAILED;
}
/*
* Obtain the interface flags from IPv6.
*/
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.
*/
if (RT_SUCCESS(rc))
{
/*
* Get the multiplexor IDs.
*/
if (!rc)
{
/*
* Get the multiplex file descriptor to the lower streams. Generally this is lost
*/
int Ip6MuxFd;
if (RT_SUCCESS(rc))
{
/*
* 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;
if (!rc)
{
/*
* Obtain the vnode from the useless userland file descriptor.
*/
if ( pIpFile
{
/*
*/
if (RT_SUCCESS(rc))
{
/*
* 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.
*/
/*
*/
if (!rc)
{
/*
* 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.
*/
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.
*/
return VINF_SUCCESS;
}
else
{
}
if (fAttach)
}
else
{
}
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d rc2=%d\n", rc, rc2));
}
else
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2));
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d rc2=%d\n"));
}
else
}
else
}
else
if (RT_SUCCESS(rc))
return rc;
return VERR_INTNET_FLT_IF_FAILED;
}
/**
* Wrapper for attaching our module to the specificed stream on the host stack.
*
* @returns VBox status code.
* @param pThis The instance.
* @param fAttach Is this an attach or detach.
*/
static int vboxNetFltSolarisModSetup(PVBOXNETFLTINS pThis, bool fAttach, VBOXNETFLTSTREAMTYPE StreamType)
{
LogFlow(("vboxNetFltSolarisModSetup: pThis=%p (%s) fAttach=%s StreamType=%d\n", pThis, pThis->szName,
switch (StreamType)
{
case kIp4Stream:
case kArpStream:
case kIp6Stream:
default:
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).
*/
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/* Ignore Ipv6 binding errors as it's optional. */
}
else
}
else
LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Vrc\n", rc));
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).
*/
{
if (pThis->u.s.pvIp6Stream)
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.
*/
{
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.
*/
{
{
}
}
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.
*/
{
unsigned cSegs = 0;
cSegs++;
#ifdef PADD_RUNT_FRAMES_FROM_HOST
cSegs++;
#endif
}
/**
* 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));
/*
* Convert the message block to segments.
*/
unsigned iSeg = 0;
while (pCur)
{
if (cbSeg)
{
iSeg++;
}
}
#ifdef PADD_RUNT_FRAMES_FROM_HOST
{
}
#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.
*/
{
return VERR_NO_MEMORY;
if (RT_UNLIKELY(!pDlpiMsg))
return VERR_NO_MEMORY;
vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset);
/* Make the message point to the protocol header */
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.
*/
{
{
return VERR_NET_PROTOCOL_ERROR;
}
/*
* We of course need to convert them into raw ethernet frames.
*/
switch (pPrim->dl_primitive)
{
case DL_UNITDATA_IND:
{
/*
* Receive side.
*/
vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
break;
}
case DL_UNITDATA_REQ:
{
/*
* Send side.
*/
vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
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.
*/
if (RT_UNLIKELY(!pEtherMsg))
return VERR_NO_MEMORY;
/*
* Change the chained blocks to type M_DATA.
*/
return VINF_SUCCESS;
}
/**
* Initializes a packet identifier.
*
* @param pTag Pointer to the packed identifier.
* @param pMsg Pointer to the message to be identified.
*
* @remarks Warning!!! This function assumes 'pMsg' is an unchained message.
*/
{
}
/**
* Queues a packet for loopback elimination.
*
* @returns VBox status code.
* @param pThis The instance.
* @param pPromiscStream Pointer to the promiscuous stream.
* @param pMsg Pointer to the message.
* @remarks Warning!! Assumes caller has taken care of any locking necessary.
*/
static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
{
LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg));
{
/*
* We don't currently make chained messages in on Xmit
* so this only needs to be supported when we do that.
*/
return VERR_NOT_SUPPORTED;
}
return VERR_NET_MSG_SIZE;
|| ( pPromiscStream->pHead
{
do
{
if (!pPromiscStream->pHead)
{
if (RT_UNLIKELY(!pCur))
{
rc = VERR_NO_MEMORY;
break;
}
break;
}
else if ( pPromiscStream->pHead
{
break;
}
else
{
if (RT_UNLIKELY(!pCur))
{
rc = VERR_NO_MEMORY;
break;
}
LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum,
break;
}
} while (0);
}
else
{
/*
* Maximum loopback queue size reached. Re-use tail as head.
*/
/*
* Find tail's previous item.
*/
/** @todo consider if this is worth switching to a double linked list... */
{
}
LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum,
}
return rc;
}
/**
* Checks if the packet is enqueued for loopback as our own packet.
*
* @returns If it's our packet, returns true after dequeuing it, otherwise false.
* @param pThis The instance.
* @param pPromiscStream Pointer to the promiscuous stream.
* @param pMsg Pointer to the message.
*/
static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
{
{
/** Handle this when Xmit makes chained messages */
return false;
}
if (cbMsg < sizeof(RTNETETHERHDR))
return false;
bool fIsOurPacket = false;
while (pCur)
{
{
continue;
}
{
continue;
}
/*
* Yes, it really is our own packet, mark it as handled
* and move it as a "free slot" to the head and return success.
*/
if (pPrev)
{
}
fIsOurPacket = true;
LogFlow((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum,
break;
}
return fIsOurPacket;
}
/**
* 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 *pMsg)
{
vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream);
if (RT_UNLIKELY(!pPromiscStream))
{
return VERR_INVALID_POINTER;
}
/*
* Don't loopback packets we transmit to the wire.
*/
/** @todo maybe we need not check for loopback for INTNETTRUNKDIR_HOST case? */
{
return VINF_SUCCESS;
}
/*
* Figure out the source of the packet based on the source Mac address.
*/
/*
* Afaik; we no longer need to worry about incorrect checksums because we now use
* checksum offloading.
*/
#if 0
if (fSrc & INTNETTRUNKDIR_HOST)
{
if (pCorrectedMsg)
}
#endif
/*
* Route all received packets into the internal network.
*/
if (RT_SUCCESS(rc))
else
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.
*/
{
if (!pStream)
return NULL;
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.
*/
{
{
return NULL;
}
{
/*
* Check if we have a complete packet or being fed a chain.
*/
size_t cbIpPacket = 0;
{
/*
* 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.
*/
if (RT_UNLIKELY(!pFullMsg))
{
LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg));
return NULL;
}
{
{
}
}
}
else
/*
* Check if the IP checksum is valid.
*/
bool fChecksumAdjusted = false;
{
/*
* Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
*/
{
{
fChecksumAdjusted = true;
}
}
{
{
fChecksumAdjusted = true;
}
}
}
if (fChecksumAdjusted)
{
/*
* If we made a copy and the checksum is corrected on the copy,
* free the original, return the checksum fixed copy.
*/
if (pFullMsg)
{
return pFullMsg;
}
return pMsg;
}
/*
* If we made a copy and the checksum is NOT corrected, free the copy,
* and return NULL.
*/
if (pFullMsg)
return NULL;
}
return NULL;
}
/**
* Simple packet dump, used for internal debugging.
*
* @param pMsg Pointer to the message to analyze and dump.
*/
{
{
{
LogFlow((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
{
{
LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
}
}
}
else
{
}
}
{
}
{
}
{
}
else
{
LogFlow((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
/* LogFlow((DEVICE_NAME ":%.*Vhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
}
}
/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
{
/*
* There is no easy way of obtaining the global host side promiscuous counter.
* Currently we just return false.
*/
return false;
}
{
}
{
/*
* MAC address change acknowledgements are intercepted on the read side
* hence theoritically we are always update to date with any changes.
*/
}
{
/*
*/
if (pStream)
{
if (pStream->pReadQueue)
{
if (RT_FAILURE(rc))
}
else
}
else
}
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
{
/* Nothing to do here. */
return VINF_SUCCESS;
}
{
{
}
}
{
/*
* Mutex used for loopback lockouts.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
return rc;
}
return rc;
}
{
/*
* Init. the solaris specific data.
*/
return VINF_SUCCESS;
}
{
/*
* We don't support interface rediscovery on Solaris hosts because the
* filter is very tightly bound to the stream.
*/
return false;
}
{
int rc = VINF_SUCCESS;
if (fDst & INTNETTRUNKDIR_WIRE)
{
{
if (pNetStack)
{
if (pNetData)
{
if (pInterface)
{
/*
* Queue out rather than direct out transmission.
*/
if (!rc)
rc = VINF_SUCCESS;
else
{
}
}
else
{
}
}
else
{
}
}
else
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
#else
vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream);
if (RT_LIKELY(pPromiscStream))
{
{
}
else
{
rc = VERR_NO_MEMORY;
}
}
#endif
}
if (fDst & INTNETTRUNKDIR_HOST)
{
/*
* For unplumbed interfaces we would not be bound to IP or ARP.
* We either bind to both or neither; so atomic reading one should be sufficient.
*/
if (!pIp4Stream)
return rc;
/*
* Create a message block and send it up the host stack (upstream).
*/
{
/*
* Send message up ARP stream.
*/
{
if (pArpStream)
{
/*
* Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path.
*/
if (RT_SUCCESS(rc))
{
}
else
{
rc = VERR_NO_MEMORY;
}
}
else
}
{
/*
* Send messages up IPv6 stream.
*/
if (pIp6Stream)
{
}
else
}
else
{
/*
* Send messages up IPv4 stream.
*/
}
}
else
{
rc = VERR_NO_MEMORY;
}
}
return rc;
}