summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c')
-rw-r--r--src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c4042
1 files changed, 4042 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
new file mode 100644
index 00000000..f455ec3c
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c
@@ -0,0 +1,4042 @@
+/* $Id: VBoxNetFlt-solaris.c $ */
+/** @file
+ * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
+#include <VBox/log.h>
+#include <VBox/err.h>
+#include <VBox/intnetinline.h>
+#include <VBox/version.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 <iprt/spinlock.h>
+#include <iprt/crc.h>
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#define VBOXNETFLT_SOLARIS_IPV6_POLLING
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+# include <iprt/timer.h>
+# include <iprt/time.h>
+#endif
+
+#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/time.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>
+#include <sys/ctf_api.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 NetDrv"
+#define DEVICE_DESC_MOD "VirtualBox NetMod"
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Driver properties */
+# define VBOXNETFLT_IP6POLLINTERVAL "ipv6-pollinterval"
+#endif
+
+/** Maximum loopback packet queue size per interface */
+#define VBOXNETFLT_LOOPBACK_SIZE 32
+
+/** VLAN tag masking, should probably be in IPRT? */
+#define VLAN_ID(vlan) (((vlan) >> 0) & 0x0fffu)
+#define VLAN_CFI(vlan) (((vlan) >> 12) & 0x0001u)
+#define VLAN_PRI(vlan) (((vlan) >> 13) & 0x0007u)
+#define VLAN_TAG(pri,cfi,vid) (((pri) << 13) | ((cfi) << 12) | ((vid) << 0))
+
+typedef struct VLANHEADER
+{
+ uint16_t Type;
+ uint16_t Data;
+} VLANHEADER;
+typedef struct VLANHEADER *PVLANHEADER;
+
+
+/*********************************************************************************************************************************
+* Global Functions *
+*********************************************************************************************************************************/
+/**
+ * Stream Driver hooks.
+ */
+static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppvResult);
+static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
+static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
+static int VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip);
+
+/**
+ * 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);
+
+
+/*********************************************************************************************************************************
+* 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 mark */
+ 0 /* lo-water mark */
+};
+
+/**
+ * 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,
+ NULL, /* service */
+ 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_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL, /* 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 */
+ VBoxNetFltSolarisQuiesceNotNeeded
+};
+
+/**
+ * modldrv: export driver specifics to kernel
+ */
+static struct modldrv g_VBoxNetFltSolarisDriver =
+{
+ &mod_driverops, /* extern from kernel */
+ DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &g_VBoxNetFltSolarisDevOps
+};
+
+/**
+ * fmodsw: streams module ops
+ */
+static struct fmodsw g_VBoxNetFltSolarisModOps =
+{
+ DEVICE_NAME,
+ &g_VBoxNetFltSolarisStreamTab,
+ D_NEW | D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL
+};
+
+/**
+ * modlstrmod: streams module specifics to kernel
+ */
+static struct modlstrmod g_VBoxNetFltSolarisModule =
+{
+ &mod_strmodops, /* extern from kernel */
+ DEVICE_DESC_MOD " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
+ &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,
+ kIp4Stream = 0x1b,
+ kIp6Stream = 0xcc,
+ kArpStream = 0xab,
+ kPromiscStream = 0xdf
+} VBOXNETFLTSTREAMTYPE;
+
+/**
+ * loopback packet identifier
+ */
+typedef struct VBOXNETFLTPACKETID
+{
+ struct VBOXNETFLTPACKETID *pNext;
+ uint16_t cbPacket;
+ uint16_t Checksum;
+ RTMAC SrcMac;
+ RTMAC DstMac;
+} VBOXNETFLTPACKETID;
+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) */
+ queue_t *pReadQueue; /* read side queue */
+ struct vboxnetflt_stream_t *pNext; /* next stream in list */
+ PVBOXNETFLTINS volatile pThis; /* the backend instance */
+ VBOXNETFLTSTREAMTYPE Type; /* the type of the stream */
+} vboxnetflt_stream_t;
+
+/**
+ * vboxnetflt_promisc_stream_t: per-interface dedicated stream data
+ */
+typedef struct vboxnetflt_promisc_stream_t
+{
+ vboxnetflt_stream_t Stream; /* dedicated/promiscuous stream */
+ bool fPromisc; /* cached promiscuous value */
+ bool fRawMode; /* whether raw mode request was successful */
+ uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ PRTTIMER pIp6Timer; /* ipv6 stream poll timer for dynamic ipv6 stream attachment */
+#endif
+ size_t cLoopback; /* loopback queue size list */
+ timeout_id_t volatile TimeoutId; /* timeout id of promisc. req */
+ PVBOXNETFLTPACKETID pHead; /* loopback packet identifier head */
+ PVBOXNETFLTPACKETID pTail; /* loopback packet identifier tail */
+} vboxnetflt_promisc_stream_t;
+
+typedef struct vboxnetflt_promisc_params_t
+{
+ PVBOXNETFLTINS pThis; /* the backend instance */
+ bool fPromiscOn; /* whether promiscuous req. on or off */
+} vboxnetflt_promisc_params_t;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream);
+/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */
+
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue);
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg);
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP);
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue);
+
+/* static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg); */
+static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg);
+
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg);
+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 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 mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg); */
+/* static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg); */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Global device info handle. */
+static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
+
+/** The (common) global data. */
+static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
+
+/** The list of all opened streams. */
+vboxnetflt_stream_t *g_VBoxNetFltSolarisStreams = NULL;
+
+/** Global mutex protecting open/close. */
+static RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+
+/** Global credentials using during open/close. */
+static cred_t *g_pVBoxNetFltSolarisCred = NULL;
+
+/**
+ * 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 = NULL;
+
+/** Goes along with the instance to determine type of stream being opened/created. */
+VBOXNETFLTSTREAMTYPE volatile g_VBoxNetFltSolarisStreamType = kUndefined;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/** Global IPv6 polling interval */
+static int g_VBoxNetFltSolarisPollInterval = -1;
+#endif
+
+static int s_off_vnode = -1;
+#define VNODE_FOR_FILE_T(filetpointer) (*(struct vnode **)((char *)(filetpointer) + s_off_vnode))
+
+
+static int
+vboxNetFltSolarisCtfGetMemberOffset(ctf_file_t *pCtfFile, const char *pszStruct, const char *pszMember, int *pOffset)
+{
+ AssertReturn(pCtfFile, VERR_INVALID_PARAMETER);
+ AssertReturn(pszStruct, VERR_INVALID_PARAMETER);
+ AssertReturn(pszMember, VERR_INVALID_PARAMETER);
+ AssertReturn(pOffset, VERR_INVALID_PARAMETER);
+
+ ctf_id_t TypeId = ctf_lookup_by_name(pCtfFile, pszStruct);
+ if (TypeId != CTF_ERR)
+ {
+ ctf_membinfo_t MemberInfo;
+ bzero(&MemberInfo, sizeof(MemberInfo));
+ if (ctf_member_info(pCtfFile, TypeId, pszMember, &MemberInfo) != CTF_ERR)
+ {
+ *pOffset = (MemberInfo.ctm_offset >> 3);
+ LogRel((DEVICE_NAME ":%s::%s at %d\n", pszStruct, pszMember, *pOffset));
+ return VINF_SUCCESS;
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_member_info failed for struct %s member %s\n", pszStruct, pszMember));
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_lookup_by_name failed for struct %s\n", pszStruct));
+
+ return VERR_NOT_FOUND;
+}
+
+
+static int
+vboxNetFltSolarisProbeCtf(void)
+{
+ /*
+ * CTF probing for fluid f_vnode member in file_t.
+ */
+ int rc = VERR_INTERNAL_ERROR;
+ modctl_t *pModCtl = mod_hold_by_name("genunix");
+ if (pModCtl)
+ {
+ int err;
+ mutex_enter(&mod_lock);
+ ctf_file_t *pCtfFile = ctf_modopen(pModCtl->mod_mp, &err);
+ mutex_exit(&mod_lock);
+ if (pCtfFile)
+ {
+ rc = vboxNetFltSolarisCtfGetMemberOffset(pCtfFile, "file_t", "f_vnode", &s_off_vnode);
+ ctf_close(pCtfFile);
+ }
+ else
+ LogRel((DEVICE_NAME ":ctf_modopen failed. err=%d\n", err));
+
+ mod_release_mod(pModCtl);
+ }
+ else
+ LogRel((DEVICE_NAME ":mod_hold_by_name failed.\n"));
+
+ return rc;
+}
+
+
+/**
+ * Kernel entry points
+ */
+int _init(void)
+{
+ LogFunc((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))
+ {
+ rc = vboxNetFltSolarisProbeCtf();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Initialize Solaris specific globals here.
+ */
+ g_VBoxNetFltSolarisStreams = NULL;
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_pVBoxNetFltSolarisCred = crdup(kcred);
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred))
+ {
+ 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 = vboxNetFltInitGlobalsAndIdc(&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));
+ vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
+
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":failed to allocate credentials.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisProbeCtf failed. rc=%d\n", rc));
+
+ RTR0Term();
+ }
+ else
+ LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
+
+ memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
+ return RTErrConvertToErrno(rc);
+}
+
+
+int _fini(void)
+{
+ int rc;
+ LogFunc((DEVICE_NAME ":_fini\n"));
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltSolarisGlobals);
+ if (RT_FAILURE(rc))
+ {
+ LogRel((DEVICE_NAME ":_fini - busy!\n"));
+ return EBUSY;
+ }
+
+ rc = mod_remove(&g_VBoxNetFltSolarisModLinkage);
+ if (!rc)
+ {
+ if (g_pVBoxNetFltSolarisCred)
+ {
+ crfree(g_pVBoxNetFltSolarisCred);
+ g_pVBoxNetFltSolarisCred = NULL;
+ }
+
+ if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
+ g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
+ }
+
+ RTR0Term();
+ }
+
+ return rc;
+}
+
+
+int _info(struct modinfo *pModInfo)
+{
+ LogFunc((DEVICE_NAME ":_info\n"));
+
+ int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
+
+ Log((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)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_ATTACH:
+ {
+ int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, CLONE_DEV);
+ if (rc == DDI_SUCCESS)
+ {
+ g_pVBoxNetFltSolarisDip = pDip;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Get the user prop. for polling interval.
+ */
+ int Interval = ddi_getprop(DDI_DEV_T_ANY, pDip, DDI_PROP_DONTPASS, VBOXNETFLT_IP6POLLINTERVAL, -1 /* default */);
+ if (Interval == -1)
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: no poll interval property specified. Skipping Ipv6 polling.\n"));
+ else if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between 1 and 120 secs.\n",
+ Interval));
+ Interval = -1;
+ }
+
+ g_VBoxNetFltSolarisPollInterval = Interval;
+#endif
+ 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;
+ }
+
+ /* case DDI_PM_RESUME: */
+ default:
+ 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)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
+
+ switch (enmCmd)
+ {
+ case DDI_DETACH:
+ {
+ ddi_remove_minor_node(pDip, NULL);
+ return DDI_SUCCESS;
+ }
+
+ case DDI_RESUME:
+ {
+ /* Nothing to do here... */
+ return DDI_SUCCESS;
+ }
+
+ /* case DDI_PM_SUSPEND: */
+ /* case DDI_HOT_PLUG_DETACH: */
+ default:
+ return DDI_FAILURE;
+ }
+}
+
+
+/**
+ * Quiesce not-needed entry point, as Solaris 10 doesn't have any
+ * ddi_quiesce_not_needed() function.
+ *
+ * @param pDip The module structure instance.
+ *
+ * @return corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisQuiesceNotNeeded(dev_info_t *pDip)
+{
+ return DDI_SUCCESS;
+}
+
+
+/**
+ * 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 **ppvResult)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd,
+ getminor((dev_t)pvArg)));
+
+ switch (enmCmd)
+ {
+ case DDI_INFO_DEVT2DEVINFO:
+ {
+ *ppvResult = g_pVBoxNetFltSolarisDip;
+ return *ppvResult ? DDI_SUCCESS : DDI_FAILURE;
+ }
+
+ case DDI_INFO_DEVT2INSTANCE:
+ {
+ /* There can only be a single-instance of this driver and thus its instance number is 0. */
+ *ppvResult = (void *)0;
+ return DDI_SUCCESS;
+ }
+ }
+
+ return DDI_FAILURE;
+}
+
+
+/**
+ * Stream module open entry point, initializes the queue and allows streams processing.
+ *
+ * @param pQueue Pointer to the read 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);
+
+ LogFunc((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 that the request was initiated by our code.
+ *
+ * This ASSUMES that crdup() will return a copy with a unique address and
+ * not do any kind of clever pooling. This check will when combined with
+ * g_VBoxNetFltSolarisMtx prevent races and that the instance gets
+ * associated with the wrong streams.
+ */
+ if (pCred != g_pVBoxNetFltSolarisCred)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid credentials.\n"));
+ return EACCES;
+ }
+
+ /*
+ * Check for the VirtualBox instance.
+ */
+ PVBOXNETFLTINS pThis = g_VBoxNetFltSolarisInstance;
+ if (!pThis)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n"));
+ return ENOENT;
+ }
+
+ /*
+ * Check VirtualBox stream type.
+ */
+ if ( g_VBoxNetFltSolarisStreamType != kPromiscStream
+ && g_VBoxNetFltSolarisStreamType != kArpStream
+ && g_VBoxNetFltSolarisStreamType != kIp6Stream
+ && g_VBoxNetFltSolarisStreamType != kIp4Stream)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode. Type=%d\n",
+ g_VBoxNetFltSolarisStreamType));
+ 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 = &g_VBoxNetFltSolarisStreams;
+ 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);
+
+ if (g_VBoxNetFltSolarisStreamType == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = RTMemAlloc(sizeof(vboxnetflt_promisc_stream_t));
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate promiscuous stream data.\n"));
+ return ENOMEM;
+ }
+
+ pPromiscStream->fPromisc = false;
+ pPromiscStream->fRawMode = false;
+ pPromiscStream->ModeReqId = 0;
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+ pPromiscStream->TimeoutId = 0;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pPromiscStream->pIp6Timer = NULL;
+#endif
+ pStream = (vboxnetflt_stream_t *)pPromiscStream;
+ }
+ else
+ {
+ /*
+ * Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
+ */
+ pStream = RTMemAlloc(sizeof(vboxnetflt_stream_t));
+ if (RT_UNLIKELY(!pStream))
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n"));
+ return ENOMEM;
+ }
+ }
+ pStream->DevMinor = DevMinor;
+ pStream->pReadQueue = pQueue;
+
+ /*
+ * Pick up the current global VBOXNETFLTINS instance as
+ * the one that we will associate this stream with.
+ */
+ ASMAtomicUoWritePtr(&pStream->pThis, pThis);
+ pStream->Type = g_VBoxNetFltSolarisStreamType;
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp4Stream, pStream); break;
+ case kIp6Stream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pIp6Stream, pStream); break;
+ case kArpStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pArpStream, pStream); break;
+ case kPromiscStream: ASMAtomicUoWritePtr((void**)&pThis->u.s.pPromiscStream, pStream); break;
+ default: /* Heh. */
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen huh!? Invalid stream type %d\n", pStream->Type));
+ RTMemFree(pStream);
+ return EINVAL;
+ }
+ }
+
+ pQueue->q_ptr = pStream;
+ WR(pQueue)->q_ptr = pStream;
+
+ /*
+ * Link it to the list of streams.
+ */
+ pStream->pNext = *ppPrevStream;
+ *ppPrevStream = pStream;
+
+ /*
+ * Increment IntNet reference count for this stream.
+ */
+ vboxNetFltRetain(pThis, false /* fBusy */);
+
+ qprocson(pQueue);
+
+ /*
+ * Don't hold the spinlocks across putnext calls as it could
+ * (and does mostly) re-enter the put procedure on the same thread.
+ */
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ /*
+ * 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.
+ */
+ int rc = vboxNetFltSolarisBindReq(pStream->pReadQueue, 0 /* SAP */);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Request the physical address (we cache the acknowledgement).
+ */
+ rc = vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue);
+ if (RT_LIKELY(RT_SUCCESS(rc)))
+ {
+ /*
+ * Ask for DLPI link notifications, don't bother check for errors here.
+ */
+ vboxNetFltSolarisNotifyReq(pStream->pReadQueue);
+
+ /*
+ * Enable raw mode.
+ */
+ rc = vboxNetFltSolarisSetRawMode(pPromiscStream);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisBindReq failed rc=%Rrc.\n", rc));
+ }
+
+ NOREF(fOpenMode);
+
+ Log((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 read 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);
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode));
+
+ vboxnetflt_stream_t *pStream = NULL;
+ vboxnetflt_stream_t **ppPrevStream = 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;
+ }
+
+ if (pStream->Type == kPromiscStream)
+ {
+ /*
+ * If there are any timeout scheduled, we need to make sure they are cancelled.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+ timeout_id_t TimeoutId = ASMAtomicReadPtr(&pPromiscStream->TimeoutId);
+ if (TimeoutId)
+ {
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), TimeoutId);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+
+ flushq(pQueue, FLUSHALL);
+ flushq(WR(pQueue), FLUSHALL);
+ }
+
+ qprocsoff(pQueue);
+
+ if (pStream->Type == kPromiscStream)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ mutex_enter(&pStream->pThis->u.s.hMtx);
+
+ /*
+ * Free-up loopback buffers.
+ */
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ while (pCur)
+ {
+ PVBOXNETFLTPACKETID pNext = pCur->pNext;
+ RTMemFree(pCur);
+ pCur = pNext;
+ }
+ pPromiscStream->pHead = NULL;
+ pPromiscStream->pTail = NULL;
+ pPromiscStream->cLoopback = 0;
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * Sheer paranoia.
+ */
+ if (pPromiscStream->pIp6Timer != NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ mutex_exit(&pStream->pThis->u.s.hMtx);
+ }
+
+ /*
+ * Unlink it from the list of streams.
+ */
+ for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
+ if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr)
+ break;
+ *ppPrevStream = pStream->pNext;
+
+ /*
+ * Delete the stream.
+ */
+ switch (pStream->Type)
+ {
+ case kIp4Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp4Stream); break;
+ case kIp6Stream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pIp6Stream); break;
+ case kArpStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pArpStream); break;
+ case kPromiscStream: ASMAtomicUoWriteNullPtr(&pStream->pThis->u.s.pPromiscStream); break;
+ default: /* Heh. */
+ {
+ AssertRelease(pStream->Type);
+ break;
+ }
+ }
+
+ /*
+ * Decrement IntNet reference count for this stream.
+ */
+ vboxNetFltRelease(pStream->pThis, false /* fBusy */);
+
+ 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.
+ * All streams, bound and unbound share this read procedure.
+ *
+ * @param pQueue Pointer to the read queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg)
+{
+ if (!pMsg)
+ return 0;
+
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ bool fSendUpstream = true;
+ vboxnetflt_stream_t *pStream = pQueue->q_ptr;
+ PVBOXNETFLTINS pThis = NULL;
+
+ /*
+ * 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 == kPromiscStream)
+ {
+ fSendUpstream = false;
+ pThis = ASMAtomicUoReadPtrT(&pStream->pThis, PVBOXNETFLTINS);
+ if (RT_LIKELY(pThis))
+ {
+ /*
+ * 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).
+ */
+ RTSpinlockAcquire(pThis->hSpinlock);
+ const bool fActive = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
+ vboxNetFltRetain(pThis, true /* fBusy */);
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
+
+ switch (DB_TYPE(pMsg))
+ {
+ case M_DATA:
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n"));
+
+ if ( fActive
+ && pPromiscStream->fRawMode)
+ {
+ vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
+ }
+ break;
+ }
+
+ case M_PROTO:
+ case M_PCPROTO:
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
+ t_uscalar_t Prim = pPrim->dl_primitive;
+
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim));
+ switch (Prim)
+ {
+ case DL_NOTIFY_IND:
+ {
+ if (MBLKL(pMsg) < DL_NOTIFY_IND_SIZE)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d"
+ " got=%d\n", DL_NOTIFY_IND_SIZE, MBLKL(pMsg)));
+ break;
+ }
+
+ dl_notify_ind_t *pNotifyInd = (dl_notify_ind_t *)pMsg->b_rptr;
+ switch (pNotifyInd->dl_notification)
+ {
+ case DL_NOTE_PHYS_ADDR:
+ {
+ if (pNotifyInd->dl_data != DL_CURR_PHYS_ADDR)
+ break;
+
+ size_t cOffset = pNotifyInd->dl_addr_offset;
+ size_t cbAddr = pNotifyInd->dl_addr_length;
+
+ if (!cOffset || !cbAddr)
+ {
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR."
+ "Invalid offset/addr.\n"));
+ fSendUpstream = false;
+ break;
+ }
+
+ bcopy(pMsg->b_rptr + cOffset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. New Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+ break;
+ }
+
+ case DL_NOTE_LINK_UP:
+ {
+ if (ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, false))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_UP.\n"));
+ break;
+ }
+
+ case DL_NOTE_LINK_DOWN:
+ {
+ if (!ASMAtomicXchgBool(&pThis->fDisconnectedFromHost, true))
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_DOWN.\n"));
+ break;
+ }
+ }
+ break;
+ }
+
+ case DL_BIND_ACK:
+ {
+ /*
+ * Swallow our bind request acknowledgement.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_BIND_ACK. Bound to requested SAP!\n"));
+ break;
+ }
+
+ case DL_PHYS_ADDR_ACK:
+ {
+ /*
+ * Swallow our physical address request acknowledgement.
+ */
+ vboxNetFltSolarisCachePhysAddr(pThis, pMsg);
+ break;
+ }
+
+ case DL_OK_ACK:
+ {
+ /*
+ * Swallow our fake promiscuous request acknowledgement.
+ */
+ dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr;
+ if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n"));
+ pPromiscStream->fPromisc = true;
+ }
+ else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ)
+ {
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n"));
+ pPromiscStream->fPromisc = 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 == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = true;
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_IOCNAK:
+ {
+ /*
+ * Swallow our fake raw/fast path mode request not acknowledged.
+ */
+ struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
+ if (pIOC->ioc_id == pPromiscStream->ModeReqId)
+ {
+ pPromiscStream->fRawMode = false;
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: WARNING! Mode not acknowledged. RawMode is %s\n",
+ pPromiscStream->fRawMode ? "ON" : "OFF"));
+ }
+ break;
+ }
+
+ case M_FLUSH:
+ {
+ /*
+ * We must support flushing queues.
+ */
+ Log((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n"));
+ if (*pMsg->b_rptr & FLUSHR)
+ flushq(pQueue, FLUSHALL);
+ break;
+ }
+ }
+
+ vboxNetFltRelease(pThis, true /* fBusy */);
+ }
+ else
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n"));
+ }
+
+ if (fSendUpstream)
+ {
+ /*
+ * Don't queue up things here, can cause bad things to happen when the system
+ * is under heavy loads and we need to jam across high priority messages which
+ * if it's not done properly will end up in an infinite loop.
+ */
+ putnext(pQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * We need to free up the message if we don't pass it through.
+ */
+ freemsg(pMsg);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Write side put procedure for processing messages in the write queue.
+ * All streams, bound and unbound share this write procedure.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg Pointer to the message.
+ *
+ * @returns corresponding solaris error code.
+ */
+static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg));
+
+ putnext(pQueue, pMsg);
+ return 0;
+}
+
+
+/**
+ * Put the stream in raw mode.
+ *
+ * @returns VBox status code.
+ * @param pPromiscStream Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pPromiscStream=%p\n", pPromiscStream));
+
+ mblk_t *pRawMsg = NULL;
+ pRawMsg = mkiocb(DLIOCRAW);
+ if (RT_UNLIKELY(!pRawMsg))
+ return VERR_NO_MEMORY;
+
+ queue_t *pQueue = pPromiscStream->Stream.pReadQueue;
+ if (!pQueue)
+ return VERR_INVALID_POINTER;
+
+ struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr;
+ pPromiscStream->ModeReqId = pIOC->ioc_id;
+ pIOC->ioc_count = 0;
+
+ qreply(pQueue, pRawMsg);
+ return VINF_SUCCESS;
+}
+
+
+#if 0
+/**
+ * Put the stream back in fast path mode.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisSetFastMode(queue_t *pQueue)
+{
+ LogFunc((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;
+}
+#endif
+
+
+/**
+ * Callback function for qwriter to send promiscuous request messages
+ * downstream.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param fPromisc Whether to send promiscuous ON or OFF requests.
+ *
+ * @returns VBox status code.
+ */
+static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc)
+{
+ LogFunc((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;
+ }
+
+ 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;
+ }
+
+ putnext(pQueue, pPromiscPhysMsg);
+ putnext(pQueue, pPromiscSapMsg);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Callback wrapper for qwriter() to safely send promiscuous requests. This is
+ * called at the outer perimeter with exclusive lock held.
+ *
+ * @param pQueue Pointer to the write queue.
+ * @param pMsg A one byte message indicates a Promisc ON, otherwise
+ * a promiscuous OFF request. See
+ * vboxNetFltSolarisPromiscReqWrap().
+ */
+static void vboxNetFltSolarisPromiscReqWrapExcl(queue_t *pQueue, mblk_t *pMsg)
+{
+ /*
+ * Paranoia.
+ */
+ AssertReturnVoid(pQueue);
+ if (RT_UNLIKELY(!pMsg))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl pQueue=%p missing message!\n", pQueue));
+
+ bool fPromisc = (MBLKL(pMsg) == 1);
+ freemsg(pMsg);
+ pMsg = NULL;
+ int rc = vboxNetFltSolarisPromiscReq(pQueue, fPromisc);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":VBoxNetFltSolarisPromiscReqWrapExcl vboxNetFltSolarisPromiscReq failed. rc=%d\n", rc));
+}
+
+
+/**
+ * Callback wrapper for qtimeout() to safely send promiscuous requests. This is
+ * called at the inner perimeter with shared lock.
+ *
+ * @param pvData Pointer to vboxnetflt_promisc_params_t. See
+ * vboxNetFltPortOsSetActive().
+ */
+static void vboxNetFltSolarisPromiscReqWrap(void *pvData)
+{
+ vboxnetflt_promisc_params_t *pParams = pvData;
+ if (RT_LIKELY(pParams))
+ {
+ PVBOXNETFLTINS pThis = pParams->pThis;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ /*
+ * Use size of message to indicate to qwriter callback whether it must send
+ * promiscuous On or Off messages. This is ugly but easier and more efficient than
+ * scheduling two separate qwriter callbacks with prepared messages to putnext.
+ */
+ size_t cbMsg = pParams->fPromiscOn ? 1 : 2;
+ mblk_t *pMsg = allocb(cbMsg, BPRI_HI);
+ if (RT_UNLIKELY(!pMsg))
+ {
+ LogRel((DEVICE_NAME ":Failed to alloc message of %u bytes\n", cbMsg));
+ return;
+ }
+
+ /*
+ * Move the data pointer so we can use MBLKL, as MBLKSIZE gets the db_lim which is
+ * always aligned.
+ */
+ pMsg->b_wptr += cbMsg;
+
+ /*
+ * Upgrade inner perimeter lock to exclusive outer perimeter lock and
+ * then call putnext while we are at the outer perimeter.
+ */
+ qwriter(WR(pPromiscStream->Stream.pReadQueue), pMsg, vboxNetFltSolarisPromiscReqWrapExcl, PERIM_OUTER);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, NULL);
+ }
+ RTMemFree(pParams);
+ }
+}
+
+
+/**
+ * Send a fake physical address request downstream.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue)
+{
+ LogFunc((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;
+
+ 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 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.
+ */
+static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg)
+{
+ LogFunc((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.MacAddr))
+ {
+ bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n",
+ sizeof(pThis->u.s.MacAddr), &pThis->u.s.MacAddr));
+
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ if (pThis->pSwitchPort)
+ pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL,
+ pPhysAddrAck->dl_addr_length));
+ }
+}
+
+
+/**
+ * Prepare DLPI bind request to a SAP.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ * @param SAP The SAP to bind the stream to.
+ */
+static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisBindReq SAP=%d\n", SAP));
+
+ mblk_t *pBindMsg = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
+ if (RT_UNLIKELY(!pBindMsg))
+ return VERR_NO_MEMORY;
+
+ dl_bind_req_t *pBindReq = (dl_bind_req_t *)pBindMsg->b_rptr;
+ pBindReq->dl_sap = SAP;
+ pBindReq->dl_max_conind = 0;
+ pBindReq->dl_conn_mgmt = 0;
+ pBindReq->dl_xidtest_flg = 0;
+ pBindReq->dl_service_mode = DL_CLDLS;
+
+ qreply(pQueue, pBindMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepare DLPI notifications request.
+ *
+ * @returns VBox status code.
+ * @param pQueue Pointer to the read queue.
+ */
+static int vboxNetFltSolarisNotifyReq(queue_t *pQueue)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisNotifyReq\n"));
+
+ mblk_t *pNotifyMsg = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ);
+ if (RT_UNLIKELY(!pNotifyMsg))
+ return VERR_NO_MEMORY;
+
+ dl_notify_req_t *pNotifyReq = (dl_notify_req_t *)pNotifyMsg->b_rptr;
+ pNotifyReq->dl_notifications = DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_PHYS_ADDR;
+
+ qreply(pQueue, pNotifyMsg);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 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
+ && pVNodeHeld)
+ {
+ TIUSER *pUser;
+ rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred);
+ if (!rc)
+ {
+ if ( pUser
+ && pUser->fp
+ && VNODE_FOR_FILE_T(pUser->fp))
+ {
+ *ppVNode = VNODE_FOR_FILE_T(pUser->fp);
+ *ppVNodeHeld = pVNodeHeld;
+ *ppUser = pUser;
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev failed. pUser=%p fp=%p f_vnode=%p\n", pUser,
+ pUser ? pUser->fp : NULL, pUser && pUser->fp ? VNODE_FOR_FILE_T(pUser->fp) : NULL));
+ }
+
+ if (pUser)
+ t_kclose(pUser, 0);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev t_kopen failed. rc=%d\n", rc));
+
+ VN_RELE(pVNodeHeld);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenDev lookupname failed. rc=%d pVNodeHeld=%p\n", rc, 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);
+}
+
+
+/**
+ * Set the DLPI style-2 PPA via an attach request, Synchronous.
+ * Waits for request acknowledgement and verifies the result.
+ *
+ * @returns VBox status code.
+ * @param hDevice Layered device handle.
+ * @param PPA Physical Point of Attachment (PPA) number.
+ */
+static int vboxNetFltSolarisAttachReq(ldi_handle_t hDevice, int PPA)
+{
+ int rc;
+ mblk_t *pAttachMsg = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ);
+ if (RT_UNLIKELY(!pAttachMsg))
+ return VERR_NO_MEMORY;
+
+ dl_attach_req_t *pAttachReq = (dl_attach_req_t *)pAttachMsg->b_rptr;
+ pAttachReq->dl_ppa = PPA;
+
+ rc = ldi_putmsg(hDevice, pAttachMsg);
+ if (!rc)
+ {
+ rc = ldi_getmsg(hDevice, &pAttachMsg, NULL);
+ if (!rc)
+ {
+ /*
+ * Verify if the attach succeeded.
+ */
+ size_t cbMsg = MBLKL(pAttachMsg);
+ if (cbMsg >= sizeof(t_uscalar_t))
+ {
+ union DL_primitives *pPrim = (union DL_primitives *)pAttachMsg->b_rptr;
+ t_uscalar_t AckPrim = pPrim->dl_primitive;
+
+ if ( AckPrim == DL_OK_ACK /* Success! */
+ && cbMsg == DL_OK_ACK_SIZE)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else if ( AckPrim == DL_ERROR_ACK /* Error Ack. */
+ && cbMsg == DL_ERROR_ACK_SIZE)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but unsupported op.\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else /* Garbled reply */
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid op."
+ " expected %d recvd %d\n", DL_OK_ACK, AckPrim));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg succeeded, but invalid size %d expected %d\n", cbMsg,
+ DL_OK_ACK_SIZE));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_getmsg failed. rc=%d\n", rc));
+ rc = VERR_INVALID_FUNCTION;
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachReq ldi_putmsg failed. rc=%d\n", rc));
+ rc = VERR_UNRESOLVED_ERROR;
+ }
+
+ freemsg(pAttachMsg);
+ return rc;
+}
+
+
+/**
+ * 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 pVNode Pointer to the device vnode.
+ * @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;
+
+ *pFd = -1; /* silence compiler warnings from -Wmaybe-uninitialized */
+ 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 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)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: 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 ":vboxNetFltSolarisRelinkIp4: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to link.\n"));
+
+ 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.
+ */
+static int vboxNetFltSolarisRelinkIp6(vnode_t *pVNode, struct lifreq *pInterface, int Ip6MuxFd)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd));
+
+ int NewIp6MuxId;
+ int rc = strioctl(pVNode, I_PLINK, (intptr_t)Ip6MuxFd, 0, K_TO_K, kcred, &NewIp6MuxId);
+ if (!rc)
+ {
+ pInterface->lifr_ip_muxid = NewIp6MuxId;
+ rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to set new Mux Id.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: 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 fAttach Is this an attach or detach.
+ * @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)
+{
+ LogFunc((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.
+ */
+ Log((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))
+ {
+ Log((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))
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
+ *pModPos = i;
+ RTMemFree(StrList.sl_modlist);
+ return VINF_SUCCESS;
+ }
+ }
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n", DEVICE_NAME));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n"));
+
+ RTMemFree(StrList.sl_modlist);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc));
+ return VERR_GENERAL_FAILURE;
+}
+
+
+/**
+ * Opens up the DLPI style 2 link that requires explicit PPA attach
+ * phase.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param pDevId Where to store the opened LDI device id.
+ */
+static int vboxNetFltSolarisOpenStyle2(PVBOXNETFLTINS pThis, ldi_ident_t *pDevId)
+{
+ /*
+ * Strip out PPA from the device name, eg: "ce3".
+ */
+ char *pszDev = RTStrDup(pThis->szName);
+ if (!pszDev)
+ return VERR_NO_MEMORY;
+
+ char *pszEnd = strchr(pszDev, '\0');
+ while (--pszEnd > pszDev)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+
+ int rc = VERR_GENERAL_FAILURE;
+ long PPA = -1;
+ if ( pszEnd
+ && ddi_strtol(pszEnd, NULL, 10, &PPA) == 0)
+ {
+ *pszEnd = '\0';
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pszDev);
+
+ /*
+ * Try open the device as DPLI style 2.
+ */
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, *pDevId);
+ if (!rc)
+ {
+ /*
+ * Attach the PPA explictly.
+ */
+ rc = vboxNetFltSolarisAttachReq(pThis->u.s.hIface, (int)PPA);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pszDev);
+ return rc;
+ }
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 dl_attach failed. rc=%d szDev=%s PPA=%d rc=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to open. rc=%d szDev=%s PPA=%d\n", rc, szDev, PPA));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStyle2 Failed to construct PPA. pszDev=%s pszEnd=%s.\n", pszDev, pszEnd));
+
+ RTStrFree(pszDev);
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * 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.
+ */
+static int vboxNetFltSolarisOpenStream(PVBOXNETFLTINS pThis)
+{
+ ldi_ident_t DevId;
+ DevId = ldi_ident_from_anon();
+ int ret;
+
+ /*
+ * Figure out if this is a VLAN interface or not based on the interface name.
+ * Only works for the VLAN PPA-hack based names. See @bugref{4854} for details.
+ */
+ char *pszEnd = strchr(pThis->szName, '\0');
+ while (--pszEnd > pThis->szName)
+ if (!RT_C_IS_DIGIT(*pszEnd))
+ break;
+ pszEnd++;
+ uint32_t PPA = RTStrToUInt32(pszEnd);
+ if (PPA > 1000)
+ {
+ pThis->u.s.fVLAN = true;
+ LogRel((DEVICE_NAME ": %s detected as VLAN interface with VID=%u.\n", pThis->szName, PPA / 1000U));
+ }
+
+ /*
+ * Try style-1 open first.
+ */
+ char szDev[128];
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/net/%s", pThis->szName);
+ int rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ if ( rc
+ && rc == ENODEV) /* ENODEV is returned when resolvepath fails, not ENOENT */
+ {
+ /*
+ * Fallback to non-ClearView style-1 open.
+ */
+ RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pThis->szName);
+ rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
+ }
+
+ if (rc)
+ {
+ /*
+ * Try DLPI style 2.
+ */
+ rc = vboxNetFltSolarisOpenStyle2(pThis, &DevId);
+ if (RT_FAILURE(rc))
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream vboxNetFltSolarisOpenStyle2 failed. rc=%d\n", rc));
+ else
+ rc = 0;
+ }
+
+ ldi_ident_release(DevId);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to open '%s' rc=%d pszName='%s'\n", szDev, rc, pThis->szName));
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_FIND, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret);
+ if (!rc)
+ {
+ if (!ret)
+ {
+ if (RT_LIKELY(g_pVBoxNetFltSolarisCred)) /* Paranoia */
+ {
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kPromiscStream;
+
+ rc = ldi_ioctl(pThis->u.s.hIface, I_PUSH, (intptr_t)DEVICE_NAME, FKIOCTL, g_pVBoxNetFltSolarisCred, &ret);
+
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream huh!? Missing credentials.\n"));
+ rc = VERR_INVALID_POINTER;
+ }
+
+ 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));
+
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+
+ return VERR_INTNET_FLT_IF_FAILED;
+}
+
+
+/**
+ * Closes the interface, thereby closing the dedicated stream.
+ *
+ * @param pThis The instance.
+ */
+static void vboxNetFltSolarisCloseStream(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisCloseStream pThis=%p\n"));
+
+ if (pThis->u.s.hIface)
+ {
+ ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
+ pThis->u.s.hIface = NULL;
+ }
+}
+
+
+/**
+ * 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.
+ */
+static int vboxNetFltSolarisAttachIp4(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp4 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip4Interface;
+ bzero(&Ip4Interface, sizeof(Ip4Interface));
+ Ip4Interface.lifr_addr.ss_family = AF_INET;
+ strncpy(Ip4Interface.lifr_name, pThis->szName, sizeof(Ip4Interface.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 DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip4DevHandle;
+ ldi_handle_t ArpDevHandle;
+
+ /*
+ * Open the IP and ARP streams as layered devices.
+ */
+ rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &Ip4DevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ArpDevHandle, DeviceIdent);
+ if (rc)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName));
+ ldi_ident_release(DeviceIdent);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ return VERR_INTNET_FLT_IF_FAILED;
+ }
+
+ ldi_ident_release(DeviceIdent);
+
+ /*
+ * Obtain the interface flags from IPv4.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip4DevHandle, &Ip4Interface);
+ 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 *pUdp4VNode = NULL;
+ vnode_t *pUdp4VNodeHeld = NULL;
+ TIUSER *pUdp4User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pUdp4VNode, &pUdp4VNodeHeld, &pUdp4User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip4DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip4Interface, 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 Ip4MuxFd;
+ int ArpMuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_ip_muxid, &Ip4MuxFd);
+ rc2 = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.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.
+ */
+ rc = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &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.
+ */
+ file_t *pIpFile = getf(Ip4MuxFd);
+ file_t *pArpFile = getf(ArpMuxFd);
+ if ( pIpFile
+ && pArpFile
+ && VNODE_FOR_FILE_T(pArpFile)
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp4VNode = VNODE_FOR_FILE_T(pIpFile);
+ vnode_t *pArpVNode = VNODE_FOR_FILE_T(pArpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp4VNode, &StrMod.pos);
+ rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pArpVNode, &ArpStrMod.pos);
+ if ( RT_SUCCESS(rc)
+ && RT_SUCCESS(rc2))
+ {
+ /*
+ * Inject/Eject from the host IP stack.
+ */
+
+ /*
+ * 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.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp4Stream;
+ }
+
+ rc = strioctl(pIp4VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ if (!rc)
+ {
+ /*
+ * Inject/Eject from the host ARP stack.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kArpStream;
+ }
+
+ rc = strioctl(pArpVNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ 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.
+ */
+ rc = vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, 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(pUdp4VNodeHeld, pUdp4User);
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Success! %s %s@(IPv4:%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 ":vboxNetFltSolarisAttachIp4: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ /*
+ * Try failing gracefully during attach.
+ */
+ if (fAttach)
+ strioctl(pArpVNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the ARP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp4VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc,
+ rc2));
+ }
+
+ releasef(Ip4MuxFd);
+ releasef(ArpMuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get vnode from MuxFd.\n"));
+ }
+ 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", rc, rc2));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get Mux Ids. rc=%d\n", rc));
+ vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ 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;
+ }
+
+ ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
+ ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+/**
+ * Dynamically attach under IPv6 on the host stack.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ * @param fAttach Is this an attach or detach.
+ */
+static int vboxNetFltSolarisAttachIp6(PVBOXNETFLTINS pThis, bool fAttach)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachIp6 pThis=%p fAttach=%d\n", pThis, fAttach));
+
+ /*
+ * Statutory Warning: Hackish code ahead.
+ */
+ char *pszModName = DEVICE_NAME;
+
+ struct lifreq Ip6Interface;
+ bzero(&Ip6Interface, sizeof(Ip6Interface));
+ Ip6Interface.lifr_addr.ss_family = AF_INET6;
+ strncpy(Ip6Interface.lifr_name, pThis->szName, sizeof(Ip6Interface.lifr_name));
+
+ struct strmodconf StrMod;
+ StrMod.mod_name = pszModName;
+ StrMod.pos = -1; /* this is filled in later. */
+
+ int rc;
+ int ret;
+ ldi_ident_t DeviceIdent = ldi_ident_from_anon();
+ ldi_handle_t Ip6DevHandle;
+
+ /*
+ * Open the IPv6 stream as a layered devices.
+ */
+ rc = ldi_open_by_name(IP6_DEV_NAME, FREAD | FWRITE, kcred, &Ip6DevHandle, DeviceIdent);
+ ldi_ident_release(DeviceIdent);
+ 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.
+ */
+ rc = vboxNetFltSolarisGetIfFlags(Ip6DevHandle, &Ip6Interface);
+ 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 *pUdp6VNode = NULL;
+ vnode_t *pUdp6VNodeHeld = NULL;
+ TIUSER *pUdp6User = NULL;
+ rc = vboxNetFltSolarisOpenDev(UDP6_DEV_NAME, &pUdp6VNode, &pUdp6VNodeHeld, &pUdp6User);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get the multiplexor IDs.
+ */
+ rc = ldi_ioctl(Ip6DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip6Interface, 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 Ip6MuxFd;
+ rc = vboxNetFltSolarisMuxIdToFd(pUdp6VNode, Ip6Interface.lifr_ip_muxid, &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.
+ */
+ rc = strioctl(pUdp6VNode, I_PUNLINK, (intptr_t)Ip6Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
+ if (!rc)
+ {
+ /*
+ * Obtain the vnode from the useless userland file descriptor.
+ */
+ file_t *pIpFile = getf(Ip6MuxFd);
+ if ( pIpFile
+ && VNODE_FOR_FILE_T(pIpFile))
+ {
+ vnode_t *pIp6VNode = VNODE_FOR_FILE_T(pIpFile);
+
+ /*
+ * Find the position on the host stack for attaching/detaching ourselves.
+ */
+ rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp6VNode, &StrMod.pos);
+ 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.
+ */
+ rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
+ AssertRCReturn(rc, rc);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = pThis;
+ g_VBoxNetFltSolarisStreamType = kIp6Stream;
+ }
+
+ /*
+ * Inject/Eject from the host IPv6 stack.
+ */
+ rc = strioctl(pIp6VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
+ g_pVBoxNetFltSolarisCred, &ret);
+
+ if (fAttach)
+ {
+ g_VBoxNetFltSolarisInstance = NULL;
+ g_VBoxNetFltSolarisStreamType = kUndefined;
+ }
+
+ RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
+
+ 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.
+ */
+ rc = vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ 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(pUdp6VNodeHeld, pUdp6User);
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+ releasef(Ip6MuxFd);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Success! %s %s@(IPv6:%d) "
+ "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
+ StrMod.pos, fAttach ? "to" : "from", pThis->szName));
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Relinking failed. Mode=%s rc=%d.\n",
+ fAttach ? "inject" : "eject", rc));
+ }
+
+ if (fAttach)
+ strioctl(pIp6VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
+
+ vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to %s the IP stack. rc=%d\n",
+ fAttach ? "inject into" : "eject from", rc));
+ }
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d\n", rc));
+
+ releasef(Ip6MuxFd);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get vnode from MuxFd.\n"));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d.\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d\n", rc));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get Mux Ids. rc=%d\n", rc));
+
+ vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open UDP. rc=%d\n", rc));
+
+ rc = VERR_INTNET_FLT_IF_FAILED;
+ }
+ else
+ {
+ Log((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get IPv6 flags.\n", pThis->szName));
+ rc = VERR_INTNET_FLT_IF_NOT_FOUND;
+ }
+
+ ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
+
+ return rc;
+}
+
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+/**
+ * Ipv6 dynamic attachment timer callback to attach to the Ipv6 stream if needed.
+ *
+ * @param pTimer Pointer to the timer.
+ * @param pvData Opaque pointer to the instance.
+ * @param iTick Timer tick (unused).
+ */
+static void vboxNetFltSolarispIp6Timer(PRTTIMER pTimer, void *pvData, uint64_t iTick)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarispIp6Timer pTimer=%p pvData=%p\n", pTimer, pvData));
+
+ PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
+ if ( RT_LIKELY(pThis)
+ && RT_LIKELY(pTimer))
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ bool fIp6Attaching = ASMAtomicUoReadBool(&pThis->u.s.fAttaching);
+ if ( !pIp6Stream
+ && !fIp6Attaching)
+ {
+ int rc = RTSemFastMutexRequest(pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, true);
+
+ vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+ ASMAtomicUoWriteBool(&pThis->u.s.fAttaching, false);
+ RTSemFastMutexRelease(pThis->u.s.hPollMtx);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarispIp6Timer failed to obtain mutex. rc=%Rrc\n", rc));
+ }
+ }
+
+ NOREF(iTick);
+}
+
+
+/**
+ * Setups up a kernel timer based on the driver property for attaching to IPv6 stream
+ * whenever the stream gets plumbed for the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisSetupIp6Polling(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling pThis=%p\n", pThis));
+
+ int rc = VERR_GENERAL_FAILURE;
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ if (RT_LIKELY(pPromiscStream->pIp6Timer == NULL))
+ {
+ /*
+ * Validate IPv6 polling interval.
+ */
+ int Interval = g_VBoxNetFltSolarisPollInterval;
+ if (Interval < 1 || Interval > 120)
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Invalid polling interval %d. Expected between"
+ " 1 and 120 secs.\n", Interval));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Setup kernel poll timer.
+ */
+ rc = RTTimerCreateEx(&pPromiscStream->pIp6Timer, Interval * (uint64_t)1000000000, RTTIMER_FLAGS_CPU_ANY,
+ vboxNetFltSolarispIp6Timer, (void *)pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTTimerStart(pPromiscStream->pIp6Timer, 10 * (uint64_t)1000000000 /* 10 seconds to blastoff */);
+ Log((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Ipv6 %d second timer begins firing in 10 seconds.\n",
+ Interval));
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Failed to create timer. rc=%d\n", rc));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisSetupIp6Polling: Polling already started.\n"));
+ rc = VINF_SUCCESS;
+ }
+ }
+ return rc;
+}
+#endif
+
+/**
+ * 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)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisDetachFromInterface pThis=%p\n", pThis));
+
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ vboxNetFltSolarisCloseStream(pThis);
+ int rc = VINF_SUCCESS;
+ if (pThis->u.s.pIp4Stream)
+ rc = vboxNetFltSolarisAttachIp4(pThis, false /* fAttach */);
+ if (pThis->u.s.pIp6Stream)
+ rc = vboxNetFltSolarisAttachIp6(pThis, false /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->pIp6Timer == NULL)
+ {
+ RTTimerStop(pPromiscStream->pIp6Timer);
+ RTTimerDestroy(pPromiscStream->pIp6Timer);
+ ASMAtomicUoWriteNullPtr(&pPromiscStream->pIp6Timer);
+ }
+#endif
+
+ return rc;
+}
+
+
+/**
+ * Wrapper for attaching ourselves to the interface.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance.
+ */
+static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface pThis=%p\n", pThis));
+
+ /*
+ * Since this is asynchronous streams injection, let the attach succeed before we can start
+ * processing the stream.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
+ int rc = vboxNetFltSolarisOpenStream(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxNetFltSolarisAttachIp4(pThis, true /* fAttach */);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ipv6 attaching is optional and can fail. We don't bother to bring down the whole
+ * attach process just if Ipv6 interface is unavailable.
+ */
+ int rc2 = vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ /*
+ * If Ip6 interface is not plumbed and an Ip6 polling interval is specified, we need
+ * to begin polling to attach on the Ip6 interface whenever it comes up.
+ */
+ if ( rc2 == VERR_INTNET_FLT_IF_NOT_FOUND
+ && g_VBoxNetFltSolarisPollInterval != -1)
+ {
+ int rc3 = vboxNetFltSolarisSetupIp6Polling(pThis);
+ if (RT_FAILURE(rc3))
+ {
+ /*
+ * If we failed to setup Ip6 polling, warn in the release log and continue.
+ */
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface IPv6 polling inactive. rc=%Rrc\n", rc3));
+ }
+ }
+#endif
+
+ /*
+ * Report promiscuousness and capabilities.
+ */
+ if (vboxNetFltTryRetainBusyNotDisconnected(pThis))
+ {
+ Assert(pThis->pSwitchPort);
+ /** @todo There is no easy way of obtaining the global host side promiscuous
+ * counter. Currently we just return false. */
+ pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, false);
+ pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
+ pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
+ vboxNetFltRelease(pThis, true /*fBusy*/);
+ }
+
+ /*
+ * Ipv4 is successful, and maybe Ipv6, we're ready for transfers.
+ */
+ ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false);
+
+ return VINF_SUCCESS;
+ }
+
+ vboxNetFltSolarisCloseStream(pThis);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Rrc\n", rc));
+
+ 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.
+ * @param fDst The destination mask, INTNETTRUNKDIR_XXX. Ignored.
+ */
+static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n", pThis, pSG));
+
+ 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)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
+
+ /*
+ * Convert the message block to segments. Work INTNETSG::cbTotal.
+ */
+ IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
+ 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))
+ {
+ Log((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++;
+ Assert(iSeg + 1 < cSegs);
+ }
+#endif
+
+ Log((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 ppDlpiMsg 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)
+{
+ LogFunc((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;
+}
+
+#if 0
+/**
+ * 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)
+{
+ LogFunc((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.MacAddr, &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;
+}
+#endif
+
+/**
+ * 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.
+ */
+static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg)
+{
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ size_t cbMsg = MBLKL(pMsg);
+
+ pTag->cbPacket = cbMsg;
+ pTag->Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ bcopy(&pEthHdr->SrcMac, &pTag->SrcMac, sizeof(RTMAC));
+ bcopy(&pEthHdr->DstMac, &pTag->DstMac, sizeof(RTMAC));
+}
+
+
+/**
+ * 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.
+ */
+static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
+{
+ Assert(pThis);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+ Assert(pPromiscStream);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg));
+
+ if (RT_UNLIKELY(pMsg->b_cont))
+ {
+ /*
+ * 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;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (RT_UNLIKELY(cbMsg < sizeof(RTNETETHERHDR)))
+ return VERR_NET_MSG_SIZE;
+
+ int rc = VINF_SUCCESS;
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pCur = NULL;
+ if (pPromiscStream->cLoopback < VBOXNETFLT_LOOPBACK_SIZE
+ || ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0))
+ {
+ do
+ {
+ if (!pPromiscStream->pHead)
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = NULL;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->pTail = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback initialized head. checksum=%u.\n",
+ pPromiscStream->pHead->Checksum));
+ break;
+ }
+ else if ( pPromiscStream->pHead
+ && pPromiscStream->pHead->cbPacket == 0)
+ {
+ pCur = pPromiscStream->pHead;
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback re-used head checksum=%u cLoopback=%d.\n",
+ pCur->Checksum, pPromiscStream->cLoopback));
+ break;
+ }
+ else
+ {
+ pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
+ if (RT_UNLIKELY(!pCur))
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ pPromiscStream->cLoopback++;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+ } while (0);
+ }
+ else
+ {
+ /*
+ * Maximum loopback queue size reached. Re-use tail as head.
+ */
+ Assert(pPromiscStream->pHead);
+ Assert(pPromiscStream->pTail);
+
+ /*
+ * Find tail's previous item.
+ */
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ pCur = pPromiscStream->pHead;
+
+ /** @todo consider if this is worth switching to a double linked list... */
+ while (pCur != pPromiscStream->pTail)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ }
+
+ pPromiscStream->pTail = pPrev;
+ pPromiscStream->pTail->pNext = NULL;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+
+ vboxNetFltSolarisInitPacketId(pCur, pMsg);
+ Log((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum,
+ pPromiscStream->cLoopback));
+ }
+
+ mutex_exit(&pThis->u.s.hMtx);
+
+ 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)
+{
+ Assert(pThis);
+ Assert(pPromiscStream);
+ Assert(pMsg);
+ Assert(DB_TYPE(pMsg) == M_DATA);
+
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ if (pMsg->b_cont)
+ {
+ /** Handle this when Xmit makes chained messages */
+ return false;
+ }
+
+ size_t cbMsg = MBLKL(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ return false;
+
+ mutex_enter(&pThis->u.s.hMtx);
+
+ PVBOXNETFLTPACKETID pPrev = NULL;
+ PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
+ bool fIsOurPacket = false;
+ while (pCur)
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ if ( pCur->cbPacket != cbMsg
+ || pCur->SrcMac.au8[0] != pEthHdr->SrcMac.au8[0]
+ || pCur->SrcMac.au8[1] != pEthHdr->SrcMac.au8[1]
+ || pCur->SrcMac.au8[2] != pEthHdr->SrcMac.au8[2]
+ || pCur->SrcMac.au8[3] != pEthHdr->SrcMac.au8[3]
+ || pCur->SrcMac.au8[4] != pEthHdr->SrcMac.au8[4]
+ || pCur->SrcMac.au8[5] != pEthHdr->SrcMac.au8[5]
+ || pCur->DstMac.au8[0] != pEthHdr->DstMac.au8[0]
+ || pCur->DstMac.au8[1] != pEthHdr->DstMac.au8[1]
+ || pCur->DstMac.au8[2] != pEthHdr->DstMac.au8[2]
+ || pCur->DstMac.au8[3] != pEthHdr->DstMac.au8[3]
+ || pCur->DstMac.au8[4] != pEthHdr->DstMac.au8[4]
+ || pCur->DstMac.au8[5] != pEthHdr->DstMac.au8[5])
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ continue;
+ }
+
+ uint16_t Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
+ if (pCur->Checksum != Checksum)
+ {
+ pPrev = pCur;
+ pCur = pCur->pNext;
+ 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.
+ */
+ pCur->cbPacket = 0;
+ if (pPrev)
+ {
+ if (!pCur->pNext)
+ pPromiscStream->pTail = pPrev;
+
+ pPrev->pNext = pCur->pNext;
+ pCur->pNext = pPromiscStream->pHead;
+ pPromiscStream->pHead = pCur;
+ }
+ fIsOurPacket = true;
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum,
+ pPromiscStream->cLoopback));
+ break;
+ }
+
+ Log((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk returns %d.\n", fIsOurPacket));
+ mutex_exit(&pThis->u.s.hMtx);
+ return fIsOurPacket;
+}
+
+
+/**
+ * Helper.
+ */
+DECLINLINE(bool) vboxNetFltPortSolarisIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
+{
+ /*
+ * MAC address change acknowledgements are intercepted on the read side
+ * hence theoretically we are always update to date with any changes.
+ */
+ return pThis->u.s.MacAddr.au16[0] == pMac->au16[0]
+ && pThis->u.s.MacAddr.au16[1] == pMac->au16[1]
+ && pThis->u.s.MacAddr.au16[2] == pMac->au16[2];
+}
+
+
+/**
+ * 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 read queue.
+ * @param pMsg Pointer to the message.
+ */
+static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pMsg=%p\n", pThis, pMsg));
+
+ AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR));
+ Assert(pStream->Type == kPromiscStream);
+
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream, vboxnetflt_promisc_stream_t *);
+ if (RT_UNLIKELY(!pPromiscStream))
+ {
+ LogRel((DEVICE_NAME ":Promiscuous stream missing!! Failing to receive packet.\n"));
+ return VERR_INVALID_POINTER;
+ }
+
+ /*
+ * Paranoia...
+ */
+ if (RT_UNLIKELY(MBLKL(pMsg) < sizeof(RTNETETHERHDR)))
+ {
+ size_t cbMsg = msgdsize(pMsg);
+ if (cbMsg < sizeof(RTNETETHERHDR))
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv %s: packet too small. Dropping packet.\n", pThis->szName));
+ return VINF_SUCCESS;
+ }
+
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ freemsg(pMsg);
+ pMsg = pFullMsg;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Don't loopback packets we transmit to the wire.
+ */
+ if (vboxNetFltSolarisIsOurMBlk(pThis, pPromiscStream, pMsg))
+ {
+ Log((DEVICE_NAME ":Avoiding packet loopback.\n"));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Figure out the source of the packet based on the source Mac address.
+ */
+ uint32_t fSrc = INTNETTRUNKDIR_WIRE;
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
+ if (vboxNetFltPortSolarisIsHostMac(pThis, &pEthHdr->SrcMac))
+ fSrc = INTNETTRUNKDIR_HOST;
+
+ /*
+ * Afaik; we no longer need to worry about incorrect checksums because we now use
+ * a dedicated stream and don't intercept packets under IP/ARP which might be doing
+ * checksum offloading.
+ */
+#if 0
+ if (fSrc & INTNETTRUNKDIR_HOST)
+ {
+ mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg);
+ if (pCorrectedMsg)
+ pMsg = pCorrectedMsg;
+ }
+ vboxNetFltSolarisAnalyzeMBlk(pMsg);
+#endif
+
+ /*
+ * Solaris raw mode streams for priority-tagged VLAN does not strip the VLAN tag.
+ * It zero's the VLAN-Id but keeps the tag intact as part of the Ethernet header.
+ * We need to manually strip these tags out or the guests might get confused.
+ */
+ bool fCopied = false;
+ bool fTagged = false;
+ if ( pThis->u.s.fVLAN
+ && pPromiscStream->fRawMode)
+ {
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ if (msgdsize(pMsg) > sizeof(RTNETETHERHDR) + sizeof(VLANHEADER))
+ {
+ if (pMsg->b_cont)
+ {
+ mblk_t *pFullMsg = msgpullup(pMsg, -1 /* all data blocks */);
+ if (pFullMsg)
+ {
+ /* Original pMsg will be freed by the caller */
+ pMsg = pFullMsg;
+ fCopied = true;
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv msgpullup failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ Log((DEVICE_NAME ":Recv VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ if ( VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)) > 0
+ && VLAN_ID(RT_BE2H_U16(pVlanHdr->Data)) == 0)
+ {
+ /*
+ * Create new Ethernet header with stripped VLAN tag.
+ */
+ size_t cbEthPrefix = sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType);
+ mblk_t *pStrippedMsg = allocb(cbEthPrefix, BPRI_MED);
+ if (RT_LIKELY(pStrippedMsg))
+ {
+ fTagged = true;
+
+ /*
+ * Copy ethernet header excluding the ethertype.
+ */
+ bcopy(pMsg->b_rptr, pStrippedMsg->b_wptr, cbEthPrefix);
+ pStrippedMsg->b_wptr += cbEthPrefix;
+
+ /*
+ * Link the rest of the message (ethertype + data, skipping VLAN header).
+ */
+ pMsg->b_rptr += cbEthPrefix + sizeof(VLANHEADER);
+ pStrippedMsg->b_cont = pMsg;
+ pMsg = pStrippedMsg;
+ Log((DEVICE_NAME ":Stripped VLAN tag.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRecv insufficient memory for creating VLAN stripped packet"
+ " cbMsg=%u.\n", cbEthPrefix));
+ if (fCopied)
+ freemsg(pMsg);
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Route all received packets into the internal network.
+ */
+ unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg);
+ PINTNETSG pSG = (PINTNETSG)alloca(RT_UOFFSETOF_DYN(INTNETSG, aSegs[cSegs]));
+ int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
+ if (RT_SUCCESS(rc))
+ pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc));
+
+ /*
+ * If we've allocated the prefix before the VLAN tag in a new message, free that.
+ */
+ if (fTagged)
+ {
+ mblk_t *pTagMsg = pMsg->b_cont;
+ pMsg->b_cont = NULL; /* b_cont could be the message from the caller or a copy we made (fCopied) */
+ freemsg(pMsg);
+ pMsg = pTagMsg;
+ }
+
+ /*
+ * If we made an extra copy for VLAN stripping, we need to free that ourselves.
+ */
+ if (fCopied)
+ freemsg(pMsg);
+
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * 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)
+{
+ LogFunc((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)
+ {
+ Log((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);
+ Log((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 NULL;
+ }
+
+ 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);
+ }
+ }
+
+ 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.
+ */
+ uint8_t *pbProtocol = (uint8_t *)(pEthHdr + 1);
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4)pbProtocol;
+ size_t cbPayload = cbIpPacket - (pIpHdr->ip_hl << 2);
+ bool fChecksumAdjusted = false;
+ if (RTNetIPv4IsHdrValid(pIpHdr, cbPayload, cbPayload))
+ {
+ pbProtocol += (pIpHdr->ip_hl << 2);
+
+ /*
+ * Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
+ */
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
+ {
+ PRTNETTCP pTcpHdr = (PRTNETTCP)pbProtocol;
+ uint16_t TcpChecksum = RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL);
+ if (pTcpHdr->th_sum != TcpChecksum)
+ {
+ pTcpHdr->th_sum = TcpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":fixed TCP checksum.\n"));
+ }
+ }
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PRTNETUDP pUdpHdr = (PRTNETUDP)pbProtocol;
+ uint16_t UdpChecksum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
+
+ if (pUdpHdr->uh_sum != UdpChecksum)
+ {
+ pUdpHdr->uh_sum = UdpChecksum;
+ fChecksumAdjusted = true;
+ Log((DEVICE_NAME ":Fixed UDP checksum."));
+ }
+ }
+ }
+
+ 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)
+ {
+ 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;
+ }
+
+ 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)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+ uint8_t *pb = 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 (!pMsg->b_cont)
+ {
+ if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
+ LogRel((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)
+ LogRel((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
+ {
+ LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
+ RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
+ }
+ }
+ }
+ else
+ {
+ Log((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
+ {
+ PVLANHEADER pVlanHdr = (PVLANHEADER)(pMsg->b_rptr + sizeof(RTNETETHERHDR) - sizeof(pEthHdr->EtherType));
+ LogRel((DEVICE_NAME ":VLAN Pcp=%u Cfi=%u Id=%u\n", VLAN_PRI(RT_BE2H_U16(pVlanHdr->Data)),
+ VLAN_CFI(RT_BE2H_U16(pVlanHdr->Data)), VLAN_ID(RT_BE2H_U16(pVlanHdr->Data))));
+ LogRel((DEVICE_NAME "%.*Rhxd\n", sizeof(VLANHEADER), pVlanHdr));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
+ LogRel((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
+ {
+ LogRel((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
+ }
+ else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
+ || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
+ {
+ LogRel((DEVICE_NAME ":IPX packet.\n"));
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
+ &pEthHdr->SrcMac));
+ /* Log((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
+ }
+}
+#endif
+
+
+/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
+
+
+
+void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
+
+ /*
+ * Enable/disable promiscuous mode.
+ */
+ vboxnetflt_promisc_params_t *pData = RTMemAllocZ(sizeof(vboxnetflt_promisc_params_t));
+ if (RT_LIKELY(pData))
+ {
+ /*
+ * See @bugref{5262} as to why we need to do all this qtimeout/qwriter tricks.
+ */
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if ( pPromiscStream
+ && pPromiscStream->Stream.pReadQueue)
+ {
+ pData->pThis = pThis;
+ pData->fPromiscOn = fActive;
+ if (ASMAtomicReadPtr(&pPromiscStream->TimeoutId))
+ quntimeout(WR(pPromiscStream->Stream.pReadQueue), pPromiscStream->TimeoutId);
+ timeout_id_t TimeoutId = qtimeout(WR(pPromiscStream->Stream.pReadQueue), vboxNetFltSolarisPromiscReqWrap,
+ pData, 1 /* ticks */);
+ ASMAtomicWritePtr(&pPromiscStream->TimeoutId, TimeoutId);
+ return; /* pData will be freed by vboxNetFltSolarisPromiscReqWrap() */
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d missing stream!\n", pThis, fActive));
+ RTMemFree(pData);
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive out of memory!\n"));
+}
+
+
+int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
+
+ vboxNetFltSolarisDetachFromInterface(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
+{
+ /* Nothing to do here. */
+ return VINF_SUCCESS;
+}
+
+
+void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ if (pThis->u.s.hPollMtx != NIL_RTSEMFASTMUTEX)
+ {
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+#endif
+
+}
+
+
+int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
+{
+ LogFunc((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n"));
+
+ /*
+ * Mutex used for loopback lockouts.
+ */
+ int rc = VINF_SUCCESS;
+ mutex_init(&pThis->u.s.hMtx, NULL /* name */, MUTEX_DRIVER, NULL /* cookie */);
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ rc = RTSemFastMutexCreate(&pThis->u.s.hPollMtx);
+ if (RT_SUCCESS(rc))
+ {
+#endif
+ rc = vboxNetFltSolarisAttachToInterface(pThis);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface failed. rc=%Rrc\n", rc));
+
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ RTSemFastMutexDestroy(pThis->u.s.hPollMtx);
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+ }
+ else
+ LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to create poll mutex. rc=%Rrc\n", rc));
+#endif
+
+ mutex_destroy(&pThis->u.s.hMtx);
+
+ NOREF(pvContext);
+ return rc;
+}
+
+
+int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
+{
+ /*
+ * Init. the solaris specific data.
+ */
+ pThis->u.s.hIface = NULL;
+ pThis->u.s.pIp4Stream = NULL;
+ pThis->u.s.pIp6Stream = NULL;
+ pThis->u.s.pArpStream = NULL;
+ pThis->u.s.pPromiscStream = NULL;
+ pThis->u.s.fAttaching = false;
+ pThis->u.s.fVLAN = false;
+#ifdef VBOXNETFLT_SOLARIS_IPV6_POLLING
+ pThis->u.s.hPollMtx = NIL_RTSEMFASTMUTEX;
+#endif
+ bzero(&pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
+ 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;
+}
+
+
+void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
+{
+ NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
+}
+
+
+int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
+{
+ /* Nothing to do */
+ NOREF(pThis); NOREF(pvIfData);
+ return VINF_SUCCESS;
+}
+
+
+int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
+{
+ NOREF(pvIfData);
+ LogFunc((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst));
+
+ int rc = VINF_SUCCESS;
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ {
+ vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtrT(&pThis->u.s.pPromiscStream,
+ vboxnetflt_promisc_stream_t *);
+ if (RT_LIKELY(pPromiscStream))
+ {
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n"));
+
+ vboxNetFltSolarisQueueLoopback(pThis, pPromiscStream, pMsg);
+ putnext(WR(pPromiscStream->Stream.pReadQueue), pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ 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.
+ */
+ vboxnetflt_stream_t *pIp4Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp4Stream, vboxnetflt_stream_t *);
+ if (!pIp4Stream)
+ return rc;
+
+ /*
+ * Create a message block and send it up the host stack (upstream).
+ */
+ mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
+ if (RT_LIKELY(pMsg))
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
+
+ /*
+ * Send message up ARP stream.
+ */
+ if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
+ {
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n"));
+
+ vboxnetflt_stream_t *pArpStream = ASMAtomicUoReadPtrT(&pThis->u.s.pArpStream, vboxnetflt_stream_t *);
+ if (pArpStream)
+ {
+ /*
+ * Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path.
+ */
+ mblk_t *pDlpiMsg;
+ rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg);
+ if (RT_SUCCESS(rc))
+ {
+ pMsg = pDlpiMsg;
+
+ queue_t *pArpReadQueue = pArpStream->pReadQueue;
+ putnext(pArpReadQueue, pMsg);
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed!\n"));
+ freemsg(pMsg);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ freemsg(pMsg); /* Should really never happen... */
+ }
+ else
+ {
+ vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtrT(&pThis->u.s.pIp6Stream, vboxnetflt_stream_t *);
+ if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)
+ && pIp6Stream)
+ {
+ /*
+ * Send messages up IPv6 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv6\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp6ReadQueue = pIp6Stream->pReadQueue;
+ putnext(pIp6ReadQueue, pMsg);
+ }
+ else
+ {
+ /*
+ * Send messages up IPv4 stream.
+ */
+ Log((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv4\n"));
+
+ pMsg->b_rptr += sizeof(RTNETETHERHDR);
+ queue_t *pIp4ReadQueue = pIp4Stream->pReadQueue;
+ putnext(pIp4ReadQueue, pMsg);
+ }
+ }
+ }
+ else
+ {
+ LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+ }
+
+ return rc;
+}
+