VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetFlt/solaris/VBoxNetFlt-solaris.c@ 16954

Last change on this file since 16954 was 16928, checked in by vboxsync, 16 years ago

Solaris/VBoxNetFlt: fixed panic on debug kernels.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 114.4 KB
Line 
1/* $Id: VBoxNetFlt-solaris.c 16928 2009-02-18 18:47:32Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Solaris Specific Code.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#if defined(DEBUG_ramshankar) && !defined(LOG_ENABLED)
26# define LOG_ENABLED
27#endif
28
29#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
30#include <VBox/log.h>
31#include <VBox/err.h>
32#include <VBox/cdefs.h>
33#include <VBox/version.h>
34#include <iprt/string.h>
35#include <iprt/initterm.h>
36#include <iprt/assert.h>
37#include <iprt/alloca.h>
38#include <iprt/net.h>
39#include <iprt/mem.h>
40#include <iprt/thread.h>
41#include <iprt/spinlock.h>
42#include <iprt/crc32.h>
43#include <iprt/err.h>
44
45#include <inet/ip.h>
46#include <net/if.h>
47#include <sys/socket.h>
48#include <sys/kstr.h>
49#include <sys/file.h>
50#include <sys/sockio.h>
51#include <sys/strsubr.h>
52#include <sys/pathname.h>
53#include <sys/t_kuser.h>
54
55#include <sys/types.h>
56#include <sys/dlpi.h>
57#include <sys/types.h>
58#include <sys/param.h>
59#include <sys/ethernet.h>
60#include <sys/stat.h>
61#include <sys/stream.h>
62#include <sys/stropts.h>
63#include <sys/strsun.h>
64#include <sys/modctl.h>
65#include <sys/ddi.h>
66#include <sys/sunddi.h>
67#include <sys/sunldi.h>
68
69/*
70 * Experimental: Using netinfo interfaces and queuing out packets.
71 * This is for playing better with IPFilter.
72 */
73#undef VBOXNETFLT_SOLARIS_USE_NETINFO
74#ifdef VBOXNETFLT_SOLARIS_USE_NETINFO
75# include <sys/neti.h>
76#endif
77
78// Workaround for very strange define in sys/user.h
79// #define u (curproc->p_user) /* user is now part of proc structure */
80#ifdef u
81#undef u
82#endif
83
84#define VBOXNETFLT_OS_SPECFIC 1
85#include "../VBoxNetFltInternal.h"
86
87/*******************************************************************************
88* Defined Constants And Macros *
89*******************************************************************************/
90#define VBOXSOLQUOTE2(x) #x
91#define VBOXSOLQUOTE(x) VBOXSOLQUOTE2(x)
92/** The module name. */
93#define DEVICE_NAME "vboxflt"
94/** The module descriptions as seen in 'modinfo'. */
95#define DEVICE_DESC_DRV "VirtualBox NetDrv"
96#define DEVICE_DESC_MOD "VirtualBox NetMod"
97
98/** @todo Remove the below hackery once done! */
99#if defined(DEBUG_ramshankar) && defined(LOG_ENABLED)
100# undef Log
101# define Log LogRel
102# undef LogFlow
103# define LogFlow LogRel
104#endif
105
106/** Maximum loopback packet queue size per interface */
107#define VBOXNETFLT_LOOPBACK_SIZE 32
108
109/*******************************************************************************
110* Global Functions *
111*******************************************************************************/
112/**
113 * Stream Driver hooks.
114 */
115static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
116static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
117static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
118
119/**
120 * Stream Module hooks.
121 */
122static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fFile, int fStream, cred_t *pCred);
123static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fFile, cred_t *pCred);
124static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg);
125static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg);
126
127/**
128 * OS specific hooks invoked from common VBoxNetFlt ring-0.
129 */
130bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis);
131void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac);
132bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac);
133void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive);
134int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis);
135int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis);
136void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis);
137int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis);
138int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis);
139
140
141/*******************************************************************************
142* Structures and Typedefs *
143*******************************************************************************/
144/**
145 * Streams: module info.
146 */
147static struct module_info g_VBoxNetFltSolarisModInfo =
148{
149 0xbad, /* module id */
150 DEVICE_NAME,
151 0, /* min. packet size */
152 INFPSZ, /* max. packet size */
153 0, /* hi-water mark */
154 0 /* lo-water mark */
155};
156
157/**
158 * Streams: read queue hooks.
159 */
160static struct qinit g_VBoxNetFltSolarisReadQ =
161{
162 VBoxNetFltSolarisModReadPut,
163 NULL, /* service */
164 VBoxNetFltSolarisModOpen,
165 VBoxNetFltSolarisModClose,
166 NULL, /* admin (reserved) */
167 &g_VBoxNetFltSolarisModInfo,
168 NULL /* module stats */
169};
170
171/**
172 * Streams: write queue hooks.
173 */
174static struct qinit g_VBoxNetFltSolarisWriteQ =
175{
176 VBoxNetFltSolarisModWritePut,
177 NULL, /* service */
178 NULL, /* open */
179 NULL, /* close */
180 NULL, /* admin (reserved) */
181 &g_VBoxNetFltSolarisModInfo,
182 NULL /* module stats */
183};
184
185/**
186 * Streams: IO stream tab.
187 */
188static struct streamtab g_VBoxNetFltSolarisStreamTab =
189{
190 &g_VBoxNetFltSolarisReadQ,
191 &g_VBoxNetFltSolarisWriteQ,
192 NULL, /* muxread init */
193 NULL /* muxwrite init */
194};
195
196/**
197 * cb_ops: driver char/block entry points
198 */
199static struct cb_ops g_VBoxNetFltSolarisCbOps =
200{
201 nulldev, /* cb open */
202 nulldev, /* cb close */
203 nodev, /* b strategy */
204 nodev, /* b dump */
205 nodev, /* b print */
206 nodev, /* cb read */
207 nodev, /* cb write */
208 nodev, /* cb ioctl */
209 nodev, /* c devmap */
210 nodev, /* c mmap */
211 nodev, /* c segmap */
212 nochpoll, /* c poll */
213 ddi_prop_op, /* property ops */
214 &g_VBoxNetFltSolarisStreamTab,
215 D_NEW | D_MP | D_MTQPAIR, /* compat. flag */
216 CB_REV /* revision */
217};
218
219/**
220 * dev_ops: driver entry/exit and other ops.
221 */
222static struct dev_ops g_VBoxNetFltSolarisDevOps =
223{
224 DEVO_REV, /* driver build revision */
225 0, /* ref count */
226 VBoxNetFltSolarisGetInfo,
227 nulldev, /* identify */
228 nulldev, /* probe */
229 VBoxNetFltSolarisAttach,
230 VBoxNetFltSolarisDetach,
231 nodev, /* reset */
232 &g_VBoxNetFltSolarisCbOps,
233 (struct bus_ops *)0,
234 nodev /* power */
235};
236
237/**
238 * modldrv: export driver specifics to kernel
239 */
240static struct modldrv g_VBoxNetFltSolarisDriver =
241{
242 &mod_driverops, /* extern from kernel */
243 DEVICE_DESC_DRV " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV),
244 &g_VBoxNetFltSolarisDevOps
245};
246
247/**
248 * fmodsw: streams module ops
249 */
250static struct fmodsw g_VBoxNetFltSolarisModOps =
251{
252 DEVICE_NAME,
253 &g_VBoxNetFltSolarisStreamTab,
254 D_NEW | D_MP | D_MTQPAIR
255};
256
257/**
258 * modlstrmod: streams module specifics to kernel
259 */
260static struct modlstrmod g_VBoxNetFltSolarisModule =
261{
262 &mod_strmodops, /* extern from kernel */
263 DEVICE_DESC_MOD " " VBOX_VERSION_STRING "r" VBOXSOLQUOTE(VBOX_SVN_REV),
264 &g_VBoxNetFltSolarisModOps
265};
266
267/**
268 * modlinkage: export install/remove/info to the kernel
269 */
270static struct modlinkage g_VBoxNetFltSolarisModLinkage =
271{
272 MODREV_1, /* loadable module system revision */
273 &g_VBoxNetFltSolarisDriver, /* streams driver framework */
274 &g_VBoxNetFltSolarisModule, /* streams module framework */
275 NULL /* terminate array of linkage structures */
276};
277
278struct vboxnetflt_state_t;
279
280/**
281 * vboxnetflt_dladdr_t: DL SAP address format
282 */
283typedef struct vboxnetflt_dladdr_t
284{
285 ether_addr_t Mac;
286 uint16_t SAP;
287} vboxnetflt_dladdr_t;
288
289#define VBOXNETFLT_DLADDRL sizeof(vboxnetflt_dladdr_t)
290
291/**
292 * which stream is this?
293 */
294typedef enum VBOXNETFLTSTREAMTYPE
295{
296 kUndefined = 0,
297 kIp4Stream = 0x1b,
298 kIp6Stream = 0xcc,
299 kArpStream = 0xab,
300 kPromiscStream = 0xdf
301} VBOXNETFLTSTREAMTYPE;
302
303/**
304 * loopback packet identifier
305 */
306typedef struct VBOXNETFLTPACKETID
307{
308 struct VBOXNETFLTPACKETID *pNext;
309 uint16_t cbPacket;
310 uint16_t Checksum;
311 RTMAC SrcMac;
312 RTMAC DstMac;
313} VBOXNETFLTPACKETID;
314typedef struct VBOXNETFLTPACKETID *PVBOXNETFLTPACKETID;
315
316/**
317 * vboxnetflt_stream_t: per-stream data (multiple streams per interface)
318 */
319typedef struct vboxnetflt_stream_t
320{
321 int DevMinor; /* minor device no. (for clone) */
322 queue_t *pReadQueue; /* read side queue */
323 struct vboxnetflt_stream_t *pNext; /* next stream in list */
324 PVBOXNETFLTINS volatile pThis; /* the backend instance */
325 VBOXNETFLTSTREAMTYPE Type; /* the type of the stream */
326} vboxnetflt_stream_t;
327
328/**
329 * vboxnetflt_promisc_stream_t: per-interface dedicated stream data
330 */
331typedef struct vboxnetflt_promisc_stream_t
332{
333 vboxnetflt_stream_t Stream; /* The generic stream */
334 bool fPromisc; /* cached promiscous value */
335 bool fRawMode; /* whether raw mode request was successful */
336 uint32_t ModeReqId; /* track MIOCTLs for swallowing our fake request acknowledgements */
337 size_t cLoopback; /* loopback queue size list */
338 PVBOXNETFLTPACKETID pHead; /* loopback packet identifier head */
339 PVBOXNETFLTPACKETID pTail; /* loopback packet identifier tail */
340} vboxnetflt_promisc_stream_t;
341
342
343/*******************************************************************************
344* Internal Functions *
345*******************************************************************************/
346static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream);
347/* static int vboxNetFltSolarisSetFastMode(queue_t *pQueue); */
348
349static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue);
350static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pPhysAddrAckMsg);
351static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP);
352static int vboxNetFltSolarisNotifyReq(queue_t *pQueue);
353
354static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg);
355static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg);
356
357static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg);
358static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
359static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg);
360
361static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst);
362static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg);
363static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc);
364static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg);
365static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg);
366static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg);
367
368
369/*******************************************************************************
370* Global Variables *
371*******************************************************************************/
372/** Global device info handle. */
373static dev_info_t *g_pVBoxNetFltSolarisDip = NULL;
374
375/** The (common) global data. */
376static VBOXNETFLTGLOBALS g_VBoxNetFltSolarisGlobals;
377
378/** The list of all opened streams. */
379vboxnetflt_stream_t *g_VBoxNetFltSolarisStreams;
380
381/** Global mutex protecting open/close. */
382static RTSEMFASTMUTEX g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
383
384/**
385 * g_VBoxNetFltInstance is the current PVBOXNETFLTINS to be associated with the stream being created
386 * in ModOpen. This is just shared global data between the dynamic attach and the ModOpen procedure.
387 */
388PVBOXNETFLTINS volatile g_VBoxNetFltSolarisInstance;
389
390/** Goes along with the instance to determine type of stream being opened/created. */
391VBOXNETFLTSTREAMTYPE volatile g_VBoxNetFltSolarisStreamType;
392
393
394/**
395 * Kernel entry points
396 */
397int _init(void)
398{
399 LogFlow((DEVICE_NAME ":_init\n"));
400
401 /*
402 * Prevent module autounloading.
403 */
404 modctl_t *pModCtl = mod_getctl(&g_VBoxNetFltSolarisModLinkage);
405 if (pModCtl)
406 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
407 else
408 LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
409
410 /*
411 * Initialize IPRT.
412 */
413 int rc = RTR0Init(0);
414 if (RT_SUCCESS(rc))
415 {
416 /*
417 * Initialize Solaris specific globals here.
418 */
419 g_VBoxNetFltSolarisStreams = NULL;
420 g_VBoxNetFltSolarisInstance = NULL;
421
422 int rc = RTSemFastMutexCreate(&g_VBoxNetFltSolarisMtx);
423 if (RT_SUCCESS(rc))
424 {
425 /*
426 * Initialize the globals and connect to the support driver.
427 *
428 * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
429 * for establishing the connect to the support driver.
430 */
431 memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
432 rc = vboxNetFltInitGlobals(&g_VBoxNetFltSolarisGlobals);
433 if (RT_SUCCESS(rc))
434 {
435 rc = mod_install(&g_VBoxNetFltSolarisModLinkage);
436 if (!rc)
437 return rc;
438
439 LogRel((DEVICE_NAME ":mod_install failed. rc=%d\n", rc));
440 vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals);
441 }
442 else
443 LogRel((DEVICE_NAME ":failed to initialize globals.\n"));
444
445 RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
446 g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
447 }
448
449 RTR0Term();
450 }
451 else
452 LogRel((DEVICE_NAME ":failed to initialize IPRT (rc=%d)\n", rc));
453
454 memset(&g_VBoxNetFltSolarisGlobals, 0, sizeof(g_VBoxNetFltSolarisGlobals));
455 return RTErrConvertToErrno(rc);
456}
457
458
459int _fini(void)
460{
461 int rc;
462 LogFlow((DEVICE_NAME ":_fini\n"));
463
464 /*
465 * Undo the work done during start (in reverse order).
466 */
467 rc = vboxNetFltTryDeleteGlobals(&g_VBoxNetFltSolarisGlobals);
468 if (RT_FAILURE(rc))
469 {
470 LogRel((DEVICE_NAME ":_fini - busy!\n"));
471 return EBUSY;
472 }
473
474 if (g_VBoxNetFltSolarisMtx != NIL_RTSEMFASTMUTEX)
475 {
476 RTSemFastMutexDestroy(g_VBoxNetFltSolarisMtx);
477 g_VBoxNetFltSolarisMtx = NIL_RTSEMFASTMUTEX;
478 }
479
480 RTR0Term();
481
482 return mod_remove(&g_VBoxNetFltSolarisModLinkage);
483}
484
485
486int _info(struct modinfo *pModInfo)
487{
488 LogFlow((DEVICE_NAME ":_info\n"));
489
490 int rc = mod_info(&g_VBoxNetFltSolarisModLinkage, pModInfo);
491
492 LogFlow((DEVICE_NAME ":_info returns %d\n", rc));
493 return rc;
494}
495
496
497/**
498 * Attach entry point, to attach a device to the system or resume it.
499 *
500 * @param pDip The module structure instance.
501 * @param enmCmd Operation type (attach/resume).
502 *
503 * @returns corresponding solaris error code.
504 */
505static int VBoxNetFltSolarisAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
506{
507 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisAttach pDip=%p enmCmd=%d\n", pDip, enmCmd));
508
509 switch (enmCmd)
510 {
511 case DDI_ATTACH:
512 {
513 int instance = ddi_get_instance(pDip);
514 int rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, CLONE_DEV);
515 if (rc == DDI_SUCCESS)
516 {
517 g_pVBoxNetFltSolarisDip = pDip;
518 ddi_report_dev(pDip);
519 return DDI_SUCCESS;
520 }
521 else
522 LogRel((DEVICE_NAME ":VBoxNetFltSolarisAttach failed to create minor node. rc%d\n", rc));
523 return DDI_FAILURE;
524 }
525
526 case DDI_RESUME:
527 {
528 /* Nothing to do here... */
529 return DDI_SUCCESS;
530 }
531 }
532 return DDI_FAILURE;
533}
534
535
536/**
537 * Detach entry point, to detach a device to the system or suspend it.
538 *
539 * @param pDip The module structure instance.
540 * @param enmCmd Operation type (detach/suspend).
541 *
542 * @returns corresponding solaris error code.
543 */
544static int VBoxNetFltSolarisDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
545{
546 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisDetach pDip=%p enmCmd=%d\n", pDip, enmCmd));
547
548 switch (enmCmd)
549 {
550 case DDI_DETACH:
551 {
552 int instance = ddi_get_instance(pDip);
553 ddi_remove_minor_node(pDip, NULL);
554 return DDI_SUCCESS;
555 }
556
557 case DDI_RESUME:
558 {
559 /* Nothing to do here... */
560 return DDI_SUCCESS;
561 }
562 }
563 return DDI_FAILURE;
564}
565
566
567/**
568 * Info entry point, called by solaris kernel for obtaining driver info.
569 *
570 * @param pDip The module structure instance (do not use).
571 * @param enmCmd Information request type.
572 * @param pvArg Type specific argument.
573 * @param ppvResult Where to store the requested info.
574 *
575 * @returns corresponding solaris error code.
576 */
577static int VBoxNetFltSolarisGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg, void **ppResult)
578{
579 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisGetInfo pDip=%p enmCmd=%d pArg=%p instance=%d\n", pDip, enmCmd,
580 getminor((dev_t)pvArg)));
581
582 switch (enmCmd)
583 {
584 case DDI_INFO_DEVT2DEVINFO:
585 {
586 *ppResult = g_pVBoxNetFltSolarisDip;
587 return DDI_SUCCESS;
588 }
589
590 case DDI_INFO_DEVT2INSTANCE:
591 {
592 int instance = getminor((dev_t)pvArg);
593 *ppResult = (void *)(uintptr_t)instance;
594 return DDI_SUCCESS;
595 }
596 }
597
598 return DDI_FAILURE;
599}
600
601
602/**
603 * Stream module open entry point, initializes the queue and allows streams processing.
604 *
605 * @param pQueue Pointer to the read queue (cannot be NULL).
606 * @param pDev Pointer to the dev_t associated with the driver at the end of the stream.
607 * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
608 * @param fStreamMode Stream open mode.
609 * @param pCred Pointer to user credentials.
610 *
611 * @returns corresponding solaris error code.
612 */
613static int VBoxNetFltSolarisModOpen(queue_t *pQueue, dev_t *pDev, int fOpenMode, int fStreamMode, cred_t *pCred)
614{
615 Assert(pQueue);
616
617 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen pQueue=%p pDev=%p fOpenMode=%d fStreamMode=%d\n", pQueue, pDev,
618 fOpenMode, fStreamMode));
619
620 int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
621 AssertRCReturn(rc, rc);
622
623 /*
624 * Already open?
625 */
626 if (pQueue->q_ptr)
627 {
628 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen invalid open.\n"));
629 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
630 return ENOENT;
631 }
632
633 /*
634 * Check for the VirtualBox instance.
635 */
636 PVBOXNETFLTINS pThis = g_VBoxNetFltSolarisInstance;
637 if (!pThis)
638 {
639 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to get VirtualBox instance.\n"));
640 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
641 return ENOENT;
642 }
643
644 /*
645 * Check VirtualBox stream type.
646 */
647 if (g_VBoxNetFltSolarisStreamType == kUndefined)
648 {
649 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed due to undefined VirtualBox open mode.\n"));
650 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
651 return ENOENT;
652 }
653
654 /*
655 * Get minor number. For clone opens provide a new dev_t.
656 */
657 minor_t DevMinor = 0;
658 vboxnetflt_stream_t *pStream = NULL;
659 vboxnetflt_stream_t **ppPrevStream = &g_VBoxNetFltSolarisStreams;
660 if (fStreamMode == CLONEOPEN)
661 {
662 for (; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
663 {
664 if (DevMinor < pStream->DevMinor)
665 break;
666 DevMinor++;
667 }
668 *pDev = makedevice(getmajor(*pDev), DevMinor);
669 }
670 else
671 DevMinor = getminor(*pDev);
672
673 if (g_VBoxNetFltSolarisStreamType == kPromiscStream)
674 {
675 vboxnetflt_promisc_stream_t *pPromiscStream = RTMemAlloc(sizeof(vboxnetflt_promisc_stream_t));
676 if (RT_UNLIKELY(!pPromiscStream))
677 {
678 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate promiscuous stream data.\n"));
679 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
680 return ENOMEM;
681 }
682
683 pPromiscStream->fPromisc = false;
684 pPromiscStream->fRawMode = false;
685 pPromiscStream->ModeReqId = 0;
686 pPromiscStream->pHead = NULL;
687 pPromiscStream->pTail = NULL;
688 pPromiscStream->cLoopback = 0;
689 pStream = (vboxnetflt_stream_t *)pPromiscStream;
690 }
691 else
692 {
693 /*
694 * Allocate & initialize per-stream data. Hook it into the (read and write) queue's module specific data.
695 */
696 pStream = RTMemAlloc(sizeof(vboxnetflt_stream_t));
697 if (RT_UNLIKELY(!pStream))
698 {
699 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModOpen failed to allocate stream data.\n"));
700 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
701 return ENOMEM;
702 }
703 }
704 pStream->DevMinor = DevMinor;
705 pStream->pReadQueue = pQueue;
706
707 /*
708 * Pick up the current global VBOXNETFLTINS instance as
709 * the one that we will associate this stream with.
710 */
711 ASMAtomicUoWritePtr((void * volatile *)&pStream->pThis, pThis);
712 pStream->Type = g_VBoxNetFltSolarisStreamType;
713 switch (pStream->Type)
714 {
715 case kIp4Stream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvIp4Stream, pStream); break;
716 case kIp6Stream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvIp6Stream, pStream); break;
717 case kArpStream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvArpStream, pStream); break;
718 case kPromiscStream: ASMAtomicUoWritePtr((void * volatile *)&pThis->u.s.pvPromiscStream, pStream); break;
719 default: /* Heh. */
720 {
721 AssertRelease(pStream->Type);
722 break;
723 }
724 }
725
726 pQueue->q_ptr = pStream;
727 WR(pQueue)->q_ptr = pStream;
728
729 /*
730 * Link it to the list of streams.
731 */
732 pStream->pNext = *ppPrevStream;
733 *ppPrevStream = pStream;
734
735 /*
736 * Release global lock, & do not hold locks across putnext calls.
737 */
738 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
739
740 qprocson(pQueue);
741
742 /*
743 * Don't hold the spinlocks across putnext calls as it could
744 * (and does mostly) re-enter the put procedure on the same thread.
745 */
746 if (pStream->Type == kPromiscStream)
747 {
748 vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
749
750 /*
751 * Bind to SAP 0 (DL_ETHER).
752 * Note: We don't support DL_TPR (token passing ring) SAP as that is unnecessary asynchronous
753 * work to get DL_INFO_REQ acknowledgements and determine SAP based on the Mac Type etc.
754 * Besides TPR doesn't really exist anymore practically as far as I know.
755 */
756 int rc = vboxNetFltSolarisBindReq(pStream->pReadQueue, 0 /* SAP */);
757 if (RT_LIKELY(RT_SUCCESS(rc)))
758 {
759 /*
760 * Request the physical address (we cache the acknowledgement).
761 */
762 rc = vboxNetFltSolarisPhysAddrReq(pStream->pReadQueue);
763 if (RT_LIKELY(RT_SUCCESS(rc)))
764 {
765 /*
766 * Ask for DLPI link notifications, don't bother check for errors here.
767 */
768 vboxNetFltSolarisNotifyReq(pStream->pReadQueue);
769
770 /*
771 * Enable raw mode.
772 */
773 rc = vboxNetFltSolarisSetRawMode(pPromiscStream);
774 if (RT_FAILURE(rc))
775 LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
776 }
777 else
778 LogRel((DEVICE_NAME ":vboxNetFltSolarisSetRawMode failed rc=%Rrc.\n", rc));
779 }
780 else
781 LogRel((DEVICE_NAME ":vboxNetFltSolarisBindReq failed rc=%Rrc.\n", rc));
782 }
783
784 NOREF(fOpenMode);
785 NOREF(pCred);
786
787 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModOpen returns 0, DevMinor=%d pQueue=%p\n", DevMinor, pStream->pReadQueue));
788
789 return 0;
790}
791
792
793/**
794 * Stream module close entry point, undoes the work done on open and closes the stream.
795 *
796 * @param pQueue Pointer to the read queue (cannot be NULL).
797 * @param fOpenMode Open mode (always 0 for streams driver, thus ignored).
798 * @param pCred Pointer to user credentials.
799 *
800 * @returns corresponding solaris error code.
801 */
802static int VBoxNetFltSolarisModClose(queue_t *pQueue, int fOpenMode, cred_t *pCred)
803{
804 Assert(pQueue);
805
806 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModClose pQueue=%p fOpenMode=%d\n", pQueue, fOpenMode));
807
808 int rc = RTSemFastMutexRequest(g_VBoxNetFltSolarisMtx);
809 AssertRCReturn(rc, rc);
810
811 vboxnetflt_stream_t *pStream = NULL;
812 vboxnetflt_stream_t **ppPrevStream = NULL;
813
814 /*
815 * Get instance data.
816 */
817 pStream = (vboxnetflt_stream_t *)pQueue->q_ptr;
818 if (RT_UNLIKELY(!pStream))
819 {
820 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModClose failed to get stream.\n"));
821 vboxNetFltRelease(pStream->pThis, false /* fBusy */);
822 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
823 return ENXIO;
824 }
825
826 if (pStream->Type == kPromiscStream)
827 {
828 flushq(pQueue, FLUSHALL);
829 flushq(WR(pQueue), FLUSHALL);
830 }
831
832 qprocsoff(pQueue);
833
834 if (pStream->Type == kPromiscStream)
835 {
836 vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
837
838 int rc = RTSemFastMutexRequest(pStream->pThis->u.s.hFastMtx);
839 AssertRCReturn(rc, rc);
840
841 /*
842 * Free-up loopback buffers.
843 */
844 PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
845 while (pCur)
846 {
847 PVBOXNETFLTPACKETID pNext = pCur->pNext;
848 RTMemFree(pCur);
849 pCur = pNext;
850 }
851 pPromiscStream->pHead = NULL;
852 pPromiscStream->pTail = NULL;
853 pPromiscStream->cLoopback = 0;
854
855 RTSemFastMutexRelease(pStream->pThis->u.s.hFastMtx);
856 }
857
858 /*
859 * Unlink it from the list of streams.
860 */
861 for (ppPrevStream = &g_VBoxNetFltSolarisStreams; (pStream = *ppPrevStream) != NULL; ppPrevStream = &pStream->pNext)
862 if (pStream == (vboxnetflt_stream_t *)pQueue->q_ptr)
863 break;
864 *ppPrevStream = pStream->pNext;
865
866 /*
867 * Delete the stream.
868 */
869 switch (pStream->Type)
870 {
871 case kIp4Stream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvIp4Stream, NULL); break;
872 case kIp6Stream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvIp6Stream, NULL); break;
873 case kArpStream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvArpStream, NULL); break;
874 case kPromiscStream: ASMAtomicUoWritePtr(pStream->pThis->u.s.pvPromiscStream, NULL); break;
875 default: /* Heh. */
876 {
877 AssertRelease(pStream->Type);
878 break;
879 }
880 }
881
882 vboxNetFltRelease(pStream->pThis, false /* fBusy */);
883 RTMemFree(pStream);
884 pQueue->q_ptr = NULL;
885 WR(pQueue)->q_ptr = NULL;
886
887 NOREF(fOpenMode);
888 NOREF(pCred);
889
890 RTSemFastMutexRelease(g_VBoxNetFltSolarisMtx);
891
892 return 0;
893}
894
895
896/**
897 * Read side put procedure for processing messages in the read queue.
898 * All streams, bound and unbound share this read procedure.
899 *
900 * @param pQueue Pointer to the read queue.
901 * @param pMsg Pointer to the message.
902 *
903 * @returns corresponding solaris error code.
904 */
905static int VBoxNetFltSolarisModReadPut(queue_t *pQueue, mblk_t *pMsg)
906{
907 if (!pMsg)
908 return 0;
909
910 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut pQueue=%p pMsg=%p\n", pQueue, pMsg));
911
912 bool fSendUpstream = true;
913 vboxnetflt_stream_t *pStream = pQueue->q_ptr;
914 PVBOXNETFLTINS pThis = NULL;
915
916 /*
917 * In the unlikely case where VirtualBox crashed and this filter
918 * is somehow still in the host stream we must try not to panic the host.
919 */
920 if ( pStream
921 && pStream->Type == kPromiscStream)
922 {
923 fSendUpstream = false;
924 pThis = ASMAtomicUoReadPtr((void * volatile *)&pStream->pThis);
925 if (RT_LIKELY(pThis))
926 {
927 /*
928 * Retain the instance if we're filtering regardless of we are active or not
929 * The reason being even when we are inactive we reference the instance (e.g
930 * the promiscuous OFF acknowledgement case).
931 */
932 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
933 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
934 const bool fActive = ASMAtomicUoReadBool(&pThis->fActive);
935 vboxNetFltRetain(pThis, true /* fBusy */);
936 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
937
938 vboxnetflt_promisc_stream_t *pPromiscStream = (vboxnetflt_promisc_stream_t *)pStream;
939
940 switch (DB_TYPE(pMsg))
941 {
942 case M_DATA:
943 {
944 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut M_DATA\n"));
945
946 if ( fActive
947 && pPromiscStream->fRawMode)
948 {
949 vboxNetFltSolarisRecv(pThis, pStream, pQueue, pMsg);
950 }
951 break;
952 }
953
954 case M_PROTO:
955 case M_PCPROTO:
956 {
957 union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
958 t_uscalar_t Prim = pPrim->dl_primitive;
959
960 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO %d\n", Prim));
961 switch (Prim)
962 {
963 case DL_NOTIFY_IND:
964 {
965 if (MBLKL(pMsg) < DL_NOTIFY_IND_SIZE)
966 {
967 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Invalid notification size; expected>=%d got=%d\n",
968 DL_NOTIFY_IND_SIZE, MBLKL(pMsg)));
969 break;
970 }
971
972 dl_notify_ind_t *pNotifyInd = (dl_notify_ind_t *)pMsg->b_rptr;
973 switch (pNotifyInd->dl_notification)
974 {
975 case DL_NOTE_PHYS_ADDR:
976 {
977 if (pNotifyInd->dl_data != DL_CURR_PHYS_ADDR)
978 break;
979
980 size_t cOffset = pNotifyInd->dl_addr_offset;
981 size_t cbAddr = pNotifyInd->dl_addr_length;
982
983 if (!cOffset || !cbAddr)
984 {
985 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. Invalid offset/addr.\n"));
986 fSendUpstream = false;
987 break;
988 }
989
990 bcopy(pMsg->b_rptr + cOffset, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac));
991 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_PHYS_ADDR. New Mac=%.*Rhxs\n",
992 sizeof(pThis->u.s.Mac), &pThis->u.s.Mac));
993 break;
994 }
995
996 case DL_NOTE_LINK_UP:
997 {
998 const bool fDisconnected = ASMAtomicUoReadBool(&pThis->fActive);
999 if (fDisconnected)
1000 {
1001 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false);
1002 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_UP.\n"));
1003 }
1004 break;
1005 }
1006
1007 case DL_NOTE_LINK_DOWN:
1008 {
1009 const bool fDisconnected = ASMAtomicUoReadBool(&pThis->fActive);
1010 if (!fDisconnected)
1011 {
1012 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
1013 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_NOTE_LINK_DOWN.\n"));
1014 }
1015 break;
1016 }
1017 }
1018 break;
1019 }
1020
1021 case DL_BIND_ACK:
1022 {
1023 /*
1024 * Swallow our bind request acknowledgement.
1025 */
1026 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: DL_BIND_ACK. Bound to requested SAP!\n"));
1027 break;
1028 }
1029
1030 case DL_PHYS_ADDR_ACK:
1031 {
1032 /*
1033 * Swallow our physical address request acknowledgement.
1034 */
1035 vboxNetFltSolarisCachePhysAddr(pThis, pMsg);
1036 break;
1037 }
1038
1039 case DL_OK_ACK:
1040 {
1041 /*
1042 * Swallow our fake promiscous request acknowledgement.
1043 */
1044 dl_ok_ack_t *pOkAck = (dl_ok_ack_t *)pMsg->b_rptr;
1045 if (pOkAck->dl_correct_primitive == DL_PROMISCON_REQ)
1046 {
1047 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is ON.\n"));
1048 pPromiscStream->fPromisc = true;
1049 }
1050 else if (pOkAck->dl_correct_primitive == DL_PROMISCOFF_REQ)
1051 {
1052 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_PCPROTO: DL_OK_ACK: fPromisc is OFF.\n"));
1053 pPromiscStream->fPromisc = false;
1054 }
1055 break;
1056 }
1057 }
1058 break;
1059 }
1060
1061 case M_IOCACK:
1062 {
1063 /*
1064 * Swallow our fake raw/fast path mode request acknowledgement.
1065 */
1066 struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
1067 if (pIOC->ioc_id == pPromiscStream->ModeReqId)
1068 {
1069 pPromiscStream->fRawMode = true;
1070 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Mode acknowledgement. RawMode is %s\n",
1071 pPromiscStream->fRawMode ? "ON" : "OFF"));
1072 }
1073 break;
1074 }
1075
1076 case M_IOCNAK:
1077 {
1078 /*
1079 * Swallow our fake raw/fast path mode request not acknowledged.
1080 */
1081 struct iocblk *pIOC = (struct iocblk *)pMsg->b_rptr;
1082 if (pIOC->ioc_id == pPromiscStream->ModeReqId)
1083 {
1084 pPromiscStream->fRawMode = false;
1085 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: WARNING! Mode not acknowledged. RawMode is %s\n",
1086 pPromiscStream->fRawMode ? "ON" : "OFF"));
1087 }
1088 break;
1089 }
1090
1091 case M_FLUSH:
1092 {
1093 /*
1094 * We must support flushing queues.
1095 */
1096 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: M_FLUSH\n"));
1097 if (*pMsg->b_rptr & FLUSHR)
1098 flushq(pQueue, FLUSHALL);
1099 break;
1100 }
1101 }
1102
1103 vboxNetFltRelease(pThis, true /* fBusy */);
1104 }
1105 else
1106 LogRel((DEVICE_NAME ":VBoxNetFltSolarisModReadPut: Could not find VirtualBox instance!!\n"));
1107 }
1108
1109 if (fSendUpstream)
1110 {
1111 /*
1112 * Don't queue up things here, can cause bad things to happen when the system
1113 * is under heavy loads and we need to jam across high priority messages which
1114 * if it's not done properly will end up in an infinite loop.
1115 */
1116 putnext(pQueue, pMsg);
1117 }
1118 else
1119 {
1120 /*
1121 * We need to free up the message if we don't pass it through.
1122 */
1123 freemsg(pMsg);
1124 }
1125
1126 return 0;
1127}
1128
1129
1130/**
1131 * Write side put procedure for processing messages in the write queue.
1132 * All streams, bound and unbound share this write procedure.
1133 *
1134 * @param pQueue Pointer to the write queue.
1135 * @param pMsg Pointer to the message.
1136 *
1137 * @returns corresponding solaris error code.
1138 */
1139static int VBoxNetFltSolarisModWritePut(queue_t *pQueue, mblk_t *pMsg)
1140{
1141 LogFlow((DEVICE_NAME ":VBoxNetFltSolarisModWritePut pQueue=%p pMsg=%p\n", pQueue, pMsg));
1142
1143 putnext(pQueue, pMsg);
1144 return 0;
1145}
1146
1147
1148/**
1149 * Put the stream in raw mode.
1150 *
1151 * @returns VBox status code.
1152 * @param pQueue Pointer to the read queue.
1153 */
1154static int vboxNetFltSolarisSetRawMode(vboxnetflt_promisc_stream_t *pPromiscStream)
1155{
1156 LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetRawMode pPromiscStream=%p\n", pPromiscStream));
1157
1158 mblk_t *pRawMsg = NULL;
1159 pRawMsg = mkiocb(DLIOCRAW);
1160 if (RT_UNLIKELY(!pRawMsg))
1161 return VERR_NO_MEMORY;
1162
1163 queue_t *pQueue = pPromiscStream->Stream.pReadQueue;
1164 if (!pQueue)
1165 return VERR_INVALID_POINTER;
1166
1167 struct iocblk *pIOC = (struct iocblk *)pRawMsg->b_rptr;
1168 pPromiscStream->ModeReqId = pIOC->ioc_id;
1169 pIOC->ioc_count = 0;
1170
1171 qreply(pQueue, pRawMsg);
1172 return VINF_SUCCESS;
1173}
1174
1175
1176#if 0
1177/**
1178 * Put the stream back in fast path mode.
1179 *
1180 * @returns VBox status code.
1181 * @param pQueue Pointer to the read queue.
1182 */
1183static int vboxNetFltSolarisSetFastMode(queue_t *pQueue)
1184{
1185 LogFlow((DEVICE_NAME ":vboxNetFltSolarisSetFastMode pQueue=%p\n", pQueue));
1186
1187 mblk_t *pFastMsg = mkiocb(DL_IOC_HDR_INFO);
1188 if (RT_UNLIKELY(!pFastMsg))
1189 return VERR_NO_MEMORY;
1190
1191 vboxnetflt_stream_t *pStream = pQueue->q_ptr;
1192 struct iocblk *pIOC = (struct iocblk *)pFastMsg->b_rptr;
1193 pStream->ModeReqId = pIOC->ioc_id;
1194
1195 size_t cbReq = sizeof(dl_unitdata_req_t) + sizeof(vboxnetflt_dladdr_t);
1196 mblk_t *pDataReqMsg = allocb(cbReq, BPRI_MED);
1197 if (RT_UNLIKELY(!pDataReqMsg))
1198 return VERR_NO_MEMORY;
1199
1200 DB_TYPE(pDataReqMsg) = M_PROTO;
1201 dl_unitdata_req_t *pDataReq = (dl_unitdata_req_t *)pDataReqMsg->b_rptr;
1202 pDataReq->dl_primitive = DL_UNITDATA_REQ;
1203 pDataReq->dl_dest_addr_length = sizeof(vboxnetflt_dladdr_t);
1204 pDataReq->dl_dest_addr_offset = sizeof(dl_unitdata_req_t);
1205 pDataReq->dl_priority.dl_min = 0;
1206 pDataReq->dl_priority.dl_max = 0;
1207
1208 bzero(pDataReqMsg->b_rptr + sizeof(dl_unitdata_req_t), sizeof(vboxnetflt_dladdr_t));
1209 pDataReqMsg->b_wptr = pDataReqMsg->b_rptr + cbReq;
1210
1211 /*
1212 * Link the data format request message into the header ioctl message.
1213 */
1214 pFastMsg->b_cont = pDataReqMsg;
1215 pIOC->ioc_count = msgdsize(pDataReqMsg);
1216
1217 qreply(pQueue, pFastMsg);
1218 return VINF_SUCCESS;
1219}
1220#endif
1221
1222
1223/**
1224 * Send fake promiscous mode requests downstream.
1225 *
1226 * @param pQueue Pointer to the read queue.
1227 * @param fPromisc Whether to enable promiscous mode or not.
1228 * @param PromiscLevel Promiscous level; DL_PROMISC_PHYS/SAP/MULTI.
1229 *
1230 * @returns VBox error code.
1231 */
1232static int vboxNetFltSolarisPromiscReq(queue_t *pQueue, bool fPromisc)
1233{
1234 LogFlow((DEVICE_NAME ":vboxNetFltSolarisPromiscReq pQueue=%p fPromisc=%d\n", pQueue, fPromisc));
1235
1236 t_uscalar_t Cmd;
1237 size_t cbReq = 0;
1238 if (fPromisc)
1239 {
1240 Cmd = DL_PROMISCON_REQ;
1241 cbReq = DL_PROMISCON_REQ_SIZE;
1242 }
1243 else
1244 {
1245 Cmd = DL_PROMISCOFF_REQ;
1246 cbReq = DL_PROMISCOFF_REQ_SIZE;
1247 }
1248
1249 mblk_t *pPromiscPhysMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
1250 if (RT_UNLIKELY(!pPromiscPhysMsg))
1251 return VERR_NO_MEMORY;
1252
1253 mblk_t *pPromiscSapMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
1254 if (RT_UNLIKELY(!pPromiscSapMsg))
1255 {
1256 freemsg(pPromiscPhysMsg);
1257 return VERR_NO_MEMORY;
1258 }
1259
1260 if (fPromisc)
1261 {
1262 ((dl_promiscon_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
1263 ((dl_promiscon_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
1264 }
1265 else
1266 {
1267 ((dl_promiscoff_req_t *)pPromiscPhysMsg->b_rptr)->dl_level = DL_PROMISC_PHYS;
1268 ((dl_promiscoff_req_t *)pPromiscSapMsg->b_rptr)->dl_level = DL_PROMISC_SAP;
1269 }
1270
1271 qreply(pQueue, pPromiscPhysMsg);
1272 qreply(pQueue, pPromiscSapMsg);
1273
1274 return VINF_SUCCESS;
1275}
1276
1277
1278/**
1279 * Send a fake physical address request downstream.
1280 *
1281 * @returns VBox status code.
1282 * @param pQueue Pointer to the read queue.
1283 * @param pMsg Pointer to the request message.
1284 */
1285static int vboxNetFltSolarisPhysAddrReq(queue_t *pQueue)
1286{
1287 LogFlow((DEVICE_NAME ":vboxNetFltSolarisPhysAddrReq pQueue=%p\n", pQueue));
1288
1289 t_uscalar_t Cmd = DL_PHYS_ADDR_REQ;
1290 size_t cbReq = DL_PHYS_ADDR_REQ_SIZE;
1291 mblk_t *pPhysAddrMsg = mexchange(NULL, NULL, cbReq, M_PROTO, Cmd);
1292 if (RT_UNLIKELY(!pPhysAddrMsg))
1293 return VERR_NO_MEMORY;
1294
1295 dl_phys_addr_req_t *pPhysAddrReq = (dl_phys_addr_req_t *)pPhysAddrMsg->b_rptr;
1296 pPhysAddrReq->dl_addr_type = DL_CURR_PHYS_ADDR;
1297
1298 qreply(pQueue, pPhysAddrMsg);
1299 return VINF_SUCCESS;
1300}
1301
1302
1303/**
1304 * Cache the MAC address into the VirtualBox instance given a physical
1305 * address acknowledgement message.
1306 *
1307 * @param pThis The instance.
1308 * @param pMsg Pointer to the physical address acknowledgement message.
1309 */
1310static void vboxNetFltSolarisCachePhysAddr(PVBOXNETFLTINS pThis, mblk_t *pMsg)
1311{
1312 LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr pThis=%p pMsg=%p\n", pThis, pMsg));
1313
1314 AssertCompile(sizeof(RTMAC) == ETHERADDRL);
1315 dl_phys_addr_ack_t *pPhysAddrAck = (dl_phys_addr_ack_t *)pMsg->b_rptr;
1316 if (pPhysAddrAck->dl_addr_length == sizeof(pThis->u.s.Mac))
1317 {
1318 bcopy(pMsg->b_rptr + pPhysAddrAck->dl_addr_offset, &pThis->u.s.Mac, sizeof(pThis->u.s.Mac));
1319
1320 LogFlow((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: DL_PHYS_ADDR_ACK: Mac=%.*Rhxs\n", sizeof(pThis->u.s.Mac),
1321 &pThis->u.s.Mac));
1322 }
1323 else
1324 {
1325 LogRel((DEVICE_NAME ":vboxNetFltSolarisCachePhysAddr: Invalid address size. expected=%d got=%d\n", ETHERADDRL,
1326 pPhysAddrAck->dl_addr_length));
1327 }
1328}
1329
1330
1331/**
1332 * Prepare DLPI bind request to a SAP.
1333 *
1334 * @returns VBox status code.
1335 * @param pQueue Pointer to the read queue.
1336 * @param SAP The SAP to bind the stream to.
1337 */
1338static int vboxNetFltSolarisBindReq(queue_t *pQueue, int SAP)
1339{
1340 LogFlow((DEVICE_NAME ":vboxNetFltSolarisBindReq SAP=%d\n", SAP));
1341
1342 mblk_t *pBindMsg = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
1343 if (RT_UNLIKELY(!pBindMsg))
1344 return VERR_NO_MEMORY;
1345
1346 dl_bind_req_t *pBindReq = (dl_bind_req_t *)pBindMsg->b_rptr;
1347 pBindReq->dl_sap = SAP;
1348 pBindReq->dl_max_conind = 0;
1349 pBindReq->dl_conn_mgmt = 0;
1350 pBindReq->dl_xidtest_flg = 0;
1351 pBindReq->dl_service_mode = DL_CLDLS;
1352
1353 qreply(pQueue, pBindMsg);
1354 return VINF_SUCCESS;
1355}
1356
1357
1358/**
1359 * Prepare DLPI notifications request.
1360 *
1361 * @returns VBox status code.
1362 * @param pQueue Pointer to the read queue.
1363 */
1364static int vboxNetFltSolarisNotifyReq(queue_t *pQueue)
1365{
1366 LogFlow((DEVICE_NAME ":vboxNetFltSolarisNotifyReq\n"));
1367
1368 mblk_t *pNotifyMsg = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ);
1369 if (RT_UNLIKELY(!pNotifyMsg))
1370 return VERR_NO_MEMORY;
1371
1372 dl_notify_req_t *pNotifyReq = (dl_notify_req_t *)pNotifyMsg->b_rptr;
1373 pNotifyReq->dl_notifications = DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_PHYS_ADDR;
1374
1375 qreply(pQueue, pNotifyMsg);
1376 return VINF_SUCCESS;
1377}
1378
1379
1380/**
1381 * Opens the required device and returns the vnode_t associated with it.
1382 * We require this for the funny attach/detach routine.
1383 *
1384 * @returns VBox status code.
1385 * @param pszDev The device path.
1386 * @param ppVNode Where to store the vnode_t pointer associated with the opened device.
1387 * @param ppVNodeHeld Where to store the vnode_t required during closing of the device.
1388 * @param ppUser Open handle required while closing the device.
1389 */
1390static int vboxNetFltSolarisOpenDev(char *pszDev, vnode_t **ppVNode, vnode_t **ppVNodeHeld, TIUSER **ppUser)
1391{
1392 int rc;
1393 vnode_t *pVNodeHeld = NULL;
1394 rc = lookupname(pszDev, UIO_SYSSPACE, FOLLOW, NULLVPP, &pVNodeHeld);
1395 if (!rc)
1396 {
1397 TIUSER *pUser;
1398 rc = t_kopen((file_t *)NULL, pVNodeHeld->v_rdev, FREAD | FWRITE, &pUser, kcred);
1399 if (!rc)
1400 {
1401 *ppVNode = pUser->fp->f_vnode;
1402 *ppVNodeHeld = pVNodeHeld;
1403 *ppUser = pUser;
1404 return VINF_SUCCESS;
1405 }
1406 VN_RELE(pVNodeHeld);
1407 }
1408 return VERR_PATH_NOT_FOUND;
1409}
1410
1411
1412/**
1413 * Close the device opened using vboxNetFltSolarisOpenDev.
1414 *
1415 * @param pVNodeHeld Pointer to the held vnode of the device.
1416 * @param pUser Pointer to the file handle.
1417 */
1418static void vboxNetFltSolarisCloseDev(vnode_t *pVNodeHeld, TIUSER *pUser)
1419{
1420 t_kclose(pUser, 0);
1421 VN_RELE(pVNodeHeld);
1422}
1423
1424
1425/**
1426 * Get the logical interface flags from the stream.
1427 *
1428 * @returns VBox status code.
1429 * @param hDevice Layered device handle.
1430 * @param pInterface Pointer to the interface.
1431 */
1432static int vboxNetFltSolarisGetIfFlags(ldi_handle_t hDevice, struct lifreq *pInterface)
1433{
1434 struct strioctl IOCReq;
1435 int rc;
1436 int ret;
1437 IOCReq.ic_cmd = SIOCGLIFFLAGS;
1438 IOCReq.ic_timout = 40;
1439 IOCReq.ic_len = sizeof(struct lifreq);
1440 IOCReq.ic_dp = (caddr_t)pInterface;
1441 rc = ldi_ioctl(hDevice, I_STR, (intptr_t)&IOCReq, FKIOCTL, kcred, &ret);
1442 if (!rc)
1443 return VINF_SUCCESS;
1444
1445 return RTErrConvertFromErrno(rc);
1446}
1447
1448
1449/**
1450 * Sets the multiplexor ID from the interface.
1451 *
1452 * @returns VBox status code.
1453 * @param pVNode Pointer to the device vnode.
1454 * @param pInterface Pointer to the interface.
1455 */
1456static int vboxNetFltSolarisSetMuxId(vnode_t *pVNode, struct lifreq *pInterface)
1457{
1458 struct strioctl IOCReq;
1459 int rc;
1460 int ret;
1461 IOCReq.ic_cmd = SIOCSLIFMUXID;
1462 IOCReq.ic_timout = 40;
1463 IOCReq.ic_len = sizeof(struct lifreq);
1464 IOCReq.ic_dp = (caddr_t)pInterface;
1465
1466 rc = strioctl(pVNode, I_STR, (intptr_t)&IOCReq, 0, K_TO_K, kcred, &ret);
1467 if (!rc)
1468 return VINF_SUCCESS;
1469
1470 return RTErrConvertFromErrno(rc);
1471}
1472
1473
1474/**
1475 * Get the multiplexor file descriptor of the lower stream.
1476 *
1477 * @returns VBox status code.
1478 * @param MuxId The multiplexor ID.
1479 * @param pFd Where to store the lower stream file descriptor.
1480 */
1481static int vboxNetFltSolarisMuxIdToFd(vnode_t *pVNode, int MuxId, int *pFd)
1482{
1483 int ret;
1484 int rc = strioctl(pVNode, _I_MUXID2FD, (intptr_t)MuxId, 0, K_TO_K, kcred, &ret);
1485 if (!rc)
1486 {
1487 *pFd = ret;
1488 return VINF_SUCCESS;
1489 }
1490
1491 return RTErrConvertFromErrno(rc);
1492}
1493
1494
1495/**
1496 * Relinks the lower and the upper IPv4 stream.
1497 *
1498 * @returns VBox status code.
1499 * @param pVNode Pointer to the device vnode.
1500 * @param pInterface Pointer to the interface.
1501 * @param IpMuxFd The IP multiplexor ID.
1502 * @param ArpMuxFd The ARP multiplexor ID.
1503 */
1504static int vboxNetFltSolarisRelinkIp4(vnode_t *pVNode, struct lifreq *pInterface, int IpMuxFd, int ArpMuxFd)
1505{
1506 LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: pVNode=%p pInterface=%p IpMuxFd=%d ArpMuxFd=%d\n", pVNode,
1507 pInterface, IpMuxFd, ArpMuxFd));
1508
1509 int NewIpMuxId;
1510 int NewArpMuxId;
1511 int rc = strioctl(pVNode, I_PLINK, (intptr_t)IpMuxFd, 0, K_TO_K, kcred, &NewIpMuxId);
1512 int rc2 = strioctl(pVNode, I_PLINK, (intptr_t)ArpMuxFd, 0, K_TO_K, kcred, &NewArpMuxId);
1513 if ( !rc
1514 && !rc2)
1515 {
1516 pInterface->lifr_ip_muxid = NewIpMuxId;
1517 pInterface->lifr_arp_muxid = NewArpMuxId;
1518 rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
1519 if (RT_SUCCESS(rc))
1520 return VINF_SUCCESS;
1521
1522 LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to set new Mux Id.\n"));
1523 }
1524 else
1525 LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp4: failed to link.\n"));
1526
1527 return VERR_GENERAL_FAILURE;
1528}
1529
1530
1531/**
1532 * Relinks the lower and the upper IPv6 stream.
1533 *
1534 * @returns VBox status code.
1535 * @param pVNode Pointer to the device vnode.
1536 * @param pInterface Pointer to the interface.
1537 * @param Ip6MuxFd The IPv6 multiplexor ID.
1538 */
1539static int vboxNetFltSolarisRelinkIp6(vnode_t *pVNode, struct lifreq *pInterface, int Ip6MuxFd)
1540{
1541 LogFlow((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: pVNode=%p pInterface=%p Ip6MuxFd=%d\n", pVNode, pInterface, Ip6MuxFd));
1542
1543 int NewIp6MuxId;
1544 int rc = strioctl(pVNode, I_PLINK, (intptr_t)Ip6MuxFd, 0, K_TO_K, kcred, &NewIp6MuxId);
1545 if (!rc)
1546 {
1547 pInterface->lifr_ip_muxid = NewIp6MuxId;
1548 rc = vboxNetFltSolarisSetMuxId(pVNode, pInterface);
1549 if (RT_SUCCESS(rc))
1550 return VINF_SUCCESS;
1551
1552 LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to set new Mux Id.\n"));
1553 }
1554 else
1555 LogRel((DEVICE_NAME ":vboxNetFltSolarisRelinkIp6: failed to link.\n"));
1556
1557 return VERR_GENERAL_FAILURE;
1558}
1559
1560
1561/**
1562 * Dynamically find the position on the host stack where to attach/detach ourselves.
1563 *
1564 * @returns VBox status code.
1565 * @param pVNode Pointer to the lower stream vnode.
1566 * @param pModPos Where to store the module position.
1567 */
1568static int vboxNetFltSolarisDetermineModPos(bool fAttach, vnode_t *pVNode, int *pModPos)
1569{
1570 LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: fAttach=%d pVNode=%p pModPos=%p\n", fAttach, pVNode, pModPos));
1571
1572 int cMod;
1573 int rc = strioctl(pVNode, I_LIST, (intptr_t)NULL, 0, K_TO_K, kcred, &cMod);
1574 if (!rc)
1575 {
1576 if (cMod < 1)
1577 {
1578 LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: too few modules on host interface. cMod=%d\n"));
1579 return VERR_OUT_OF_RANGE;
1580 }
1581
1582 /*
1583 * While attaching we make sure we are at the bottom most of the stack, excepting
1584 * the host driver.
1585 */
1586 LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: cMod=%d\n", cMod));
1587 if (fAttach)
1588 {
1589 *pModPos = cMod - 1;
1590 return VINF_SUCCESS;
1591 }
1592
1593 /*
1594 * Detaching is a bit more complicated; since user could have altered the stack positions
1595 * we take the safe approach by finding our position.
1596 */
1597 struct str_list StrList;
1598 StrList.sl_nmods = cMod;
1599 StrList.sl_modlist = RTMemAllocZ(cMod * sizeof(struct str_list));
1600 if (RT_UNLIKELY(!StrList.sl_modlist))
1601 {
1602 LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to alloc memory for StrList.\n"));
1603 return VERR_NO_MEMORY;
1604 }
1605
1606 /*
1607 * Get the list of all modules on the stack.
1608 */
1609 int ret;
1610 rc = strioctl(pVNode, I_LIST, (intptr_t)&StrList, 0, K_TO_K, kcred, &ret);
1611 if (!rc)
1612 {
1613 /*
1614 * Find our filter.
1615 */
1616 for (int i = 0; i < StrList.sl_nmods; i++)
1617 {
1618 if (!strcmp(DEVICE_NAME, StrList.sl_modlist[i].l_name))
1619 {
1620 LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: Success! Found %s at %d.\n", DEVICE_NAME, i));
1621 *pModPos = i;
1622 RTMemFree(StrList.sl_modlist);
1623 return VINF_SUCCESS;
1624 }
1625 }
1626
1627 LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to find %s in the host stack.\n"));
1628 }
1629 else
1630 LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get module information. rc=%d\n"));
1631
1632 RTMemFree(StrList.sl_modlist);
1633 }
1634 else
1635 LogRel((DEVICE_NAME ":vboxNetFltSolarisDetermineModPos: failed to get list of modules on host interface. rc=%d\n", rc));
1636 return VERR_GENERAL_FAILURE;
1637}
1638
1639
1640/**
1641 * Opens up dedicated stream on top of the interface.
1642 * As a side-effect, the stream gets opened during
1643 * the I_PUSH phase.
1644 *
1645 * @param pThis The instance.
1646 */
1647static int vboxNetFltSolarisOpenStream(PVBOXNETFLTINS pThis)
1648{
1649 ldi_ident_t DevId;
1650 DevId = ldi_ident_from_anon();
1651 int ret;
1652
1653 /** @todo support DLPI style 2.*/
1654 /*
1655 * Try style-1 open first.
1656 */
1657 char szDev[128];
1658 RTStrPrintf(szDev, sizeof(szDev), "/dev/net/%s", pThis->szName);
1659 int rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
1660 if ( rc
1661 && rc == ENODEV) /* ENODEV is returned when resolvepath fails, not ENOENT */
1662 {
1663 /*
1664 * Fallback to non-ClearView style-1 open.
1665 */
1666 RTStrPrintf(szDev, sizeof(szDev), "/dev/%s", pThis->szName);
1667 rc = ldi_open_by_name(szDev, FREAD | FWRITE, kcred, &pThis->u.s.hIface, DevId);
1668 }
1669
1670 ldi_ident_release(DevId);
1671 if (rc)
1672 {
1673 LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to open '%s' rc=%d pszName='%s'\n", szDev, rc, pThis->szName));
1674 return VERR_INTNET_FLT_IF_FAILED;
1675 }
1676
1677 rc = ldi_ioctl(pThis->u.s.hIface, I_FIND, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret);
1678 if (!rc)
1679 {
1680 if (!ret)
1681 {
1682 g_VBoxNetFltSolarisInstance = pThis;
1683 g_VBoxNetFltSolarisStreamType = kPromiscStream;
1684
1685 rc = ldi_ioctl(pThis->u.s.hIface, I_PUSH, (intptr_t)DEVICE_NAME, FKIOCTL, kcred, &ret);
1686
1687 g_VBoxNetFltSolarisInstance = NULL;
1688 g_VBoxNetFltSolarisStreamType = kUndefined;
1689
1690 if (!rc)
1691 return VINF_SUCCESS;
1692
1693 LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to push filter onto host interface '%s'\n", pThis->szName));
1694 }
1695 else
1696 return VINF_SUCCESS;
1697 }
1698 else
1699 LogRel((DEVICE_NAME ":vboxNetFltSolarisOpenStream Failed to search for filter in interface '%s'.\n", pThis->szName));
1700
1701 return VERR_INTNET_FLT_IF_FAILED;
1702}
1703
1704
1705/**
1706 * Closes the interface, thereby closing the dedicated stream.
1707 *
1708 * @param pThis The instance.
1709 */
1710static void vboxNetFltSolarisCloseStream(PVBOXNETFLTINS pThis)
1711{
1712 LogFlow((DEVICE_NAME ":vboxNetFltSolarisCloseStream pThis=%p\n"));
1713
1714 vboxNetFltRetain(pThis, false /* fBusy */);
1715
1716 ldi_close(pThis->u.s.hIface, FREAD | FWRITE, kcred);
1717}
1718
1719
1720/**
1721 * Dynamically attach under IPv4 and ARP streams on the host stack.
1722 *
1723 * @returns VBox status code.
1724 * @param pThis The instance.
1725 * @param fAttach Is this an attach or detach.
1726 */
1727static int vboxNetFltSolarisAttachIp4(PVBOXNETFLTINS pThis, bool fAttach)
1728{
1729 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp4 pThis=%p fAttach=%d\n", pThis, fAttach));
1730
1731 /*
1732 * Statuatory Warning: Hackish code ahead.
1733 */
1734 char *pszModName = DEVICE_NAME;
1735
1736 struct lifreq Ip4Interface;
1737 bzero(&Ip4Interface, sizeof(Ip4Interface));
1738 Ip4Interface.lifr_addr.ss_family = AF_INET;
1739 strncpy(Ip4Interface.lifr_name, pThis->szName, sizeof(Ip4Interface.lifr_name));
1740
1741 struct strmodconf StrMod;
1742 StrMod.mod_name = pszModName;
1743 StrMod.pos = -1; /* this is filled in later. */
1744
1745 struct strmodconf ArpStrMod;
1746 bcopy(&StrMod, &ArpStrMod, sizeof(StrMod));
1747
1748 int rc;
1749 int rc2;
1750 int ret;
1751 ldi_ident_t DeviceIdent = ldi_ident_from_anon();
1752 ldi_handle_t Ip4DevHandle;
1753 ldi_handle_t ArpDevHandle;
1754
1755 /*
1756 * Open the IP and ARP streams as layered devices.
1757 */
1758 rc = ldi_open_by_name(IP_DEV_NAME, FREAD | FWRITE, kcred, &Ip4DevHandle, DeviceIdent);
1759 if (rc)
1760 {
1761 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the IP stream on '%s'.\n", pThis->szName));
1762 ldi_ident_release(DeviceIdent);
1763 return VERR_INTNET_FLT_IF_FAILED;
1764 }
1765
1766 rc = ldi_open_by_name("/dev/arp", FREAD | FWRITE, kcred, &ArpDevHandle, DeviceIdent);
1767 if (rc)
1768 {
1769 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open the ARP stream on '%s'.\n", pThis->szName));
1770 ldi_ident_release(DeviceIdent);
1771 ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
1772 return VERR_INTNET_FLT_IF_FAILED;
1773 }
1774
1775 ldi_ident_release(DeviceIdent);
1776
1777 /*
1778 * Obtain the interface flags from IPv4.
1779 */
1780 rc = vboxNetFltSolarisGetIfFlags(Ip4DevHandle, &Ip4Interface);
1781 if (RT_SUCCESS(rc))
1782 {
1783 /*
1784 * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
1785 * things that are not possible from the layered interface.
1786 */
1787 vnode_t *pUdp4VNode = NULL;
1788 vnode_t *pUdp4VNodeHeld = NULL;
1789 TIUSER *pUdp4User = NULL;
1790 rc = vboxNetFltSolarisOpenDev(UDP_DEV_NAME, &pUdp4VNode, &pUdp4VNodeHeld, &pUdp4User);
1791 if (RT_SUCCESS(rc))
1792 {
1793 /*
1794 * Get the multiplexor IDs.
1795 */
1796 rc = ldi_ioctl(Ip4DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip4Interface, FKIOCTL, kcred, &ret);
1797 if (!rc)
1798 {
1799 /*
1800 * Get the multiplex file descriptor to the lower streams. Generally this is lost
1801 * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
1802 */
1803 int Ip4MuxFd;
1804 int ArpMuxFd;
1805 rc = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_ip_muxid, &Ip4MuxFd);
1806 rc2 = vboxNetFltSolarisMuxIdToFd(pUdp4VNode, Ip4Interface.lifr_arp_muxid, &ArpMuxFd);
1807 if ( RT_SUCCESS(rc)
1808 && RT_SUCCESS(rc2))
1809 {
1810 /*
1811 * We need to I_PUNLINK on these multiplexor IDs before we can start
1812 * operating on the lower stream as insertions are direct operations on the lower stream.
1813 */
1814 int ret;
1815 rc = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
1816 rc2 = strioctl(pUdp4VNode, I_PUNLINK, (intptr_t)Ip4Interface.lifr_arp_muxid, 0, K_TO_K, kcred, &ret);
1817 if ( !rc
1818 && !rc2)
1819 {
1820 /*
1821 * Obtain the vnode from the useless userland file descriptor.
1822 */
1823 file_t *pIpFile = getf(Ip4MuxFd);
1824 file_t *pArpFile = getf(ArpMuxFd);
1825 if ( pIpFile
1826 && pArpFile
1827 && pArpFile->f_vnode
1828 && pIpFile->f_vnode)
1829 {
1830 vnode_t *pIp4VNode = pIpFile->f_vnode;
1831 vnode_t *pArpVNode = pArpFile->f_vnode;
1832
1833 /*
1834 * Find the position on the host stack for attaching/detaching ourselves.
1835 */
1836 rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp4VNode, &StrMod.pos);
1837 rc2 = vboxNetFltSolarisDetermineModPos(fAttach, pArpVNode, &ArpStrMod.pos);
1838 if ( RT_SUCCESS(rc)
1839 && RT_SUCCESS(rc2))
1840 {
1841 /*
1842 * Inject/Eject from the host IP stack.
1843 */
1844 if (!fAttach)
1845 vboxNetFltRetain(pThis, false /* fBusy */);
1846
1847 /*
1848 * Set global data which will be grabbed by ModOpen.
1849 * There is a known (though very unlikely) race here because
1850 * of the inability to pass user data while inserting.
1851 */
1852 g_VBoxNetFltSolarisInstance = pThis;
1853 g_VBoxNetFltSolarisStreamType = kIp4Stream;
1854
1855 rc = strioctl(pIp4VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
1856 kcred, &ret);
1857
1858 g_VBoxNetFltSolarisInstance = NULL;
1859 g_VBoxNetFltSolarisStreamType = kUndefined;
1860
1861 if (!rc)
1862 {
1863 if (!fAttach)
1864 vboxNetFltRetain(pThis, false /* fBusy */);
1865
1866 /*
1867 * Inject/Eject from the host ARP stack.
1868 */
1869 g_VBoxNetFltSolarisInstance = pThis;
1870 g_VBoxNetFltSolarisStreamType = kArpStream;
1871
1872 rc = strioctl(pArpVNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&ArpStrMod, 0, K_TO_K,
1873 kcred, &ret);
1874
1875 g_VBoxNetFltSolarisInstance = NULL;
1876 g_VBoxNetFltSolarisStreamType = kUndefined;
1877
1878 if (!rc)
1879 {
1880 /*
1881 * Our job's not yet over; we need to relink the upper and lower streams
1882 * otherwise we've pretty much screwed up the host interface.
1883 */
1884 rc = vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
1885 if (RT_SUCCESS(rc))
1886 {
1887 /*
1888 * Close the devices ONLY during the return from function case; otherwise
1889 * we end up close twice which is an instant kernel panic.
1890 */
1891 vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
1892 ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
1893 ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
1894 releasef(Ip4MuxFd);
1895 releasef(ArpMuxFd);
1896
1897 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Success! %s %s@(IPv4:%d Arp:%d) "
1898 "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
1899 StrMod.pos, ArpStrMod.pos, fAttach ? "to" : "from", pThis->szName));
1900 return VINF_SUCCESS;
1901 }
1902 else
1903 {
1904 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Relinking failed. Mode=%s rc=%d.\n",
1905 fAttach ? "inject" : "eject", rc));
1906 }
1907
1908 /*
1909 * Try failing gracefully during attach.
1910 */
1911 if (fAttach)
1912 strioctl(pArpVNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
1913 }
1914 else
1915 {
1916 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the ARP stack. rc=%d\n",
1917 fAttach ? "inject into" : "eject from", rc));
1918 }
1919
1920 if (fAttach)
1921 strioctl(pIp4VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
1922
1923 vboxNetFltSolarisRelinkIp4(pUdp4VNode, &Ip4Interface, Ip4MuxFd, ArpMuxFd);
1924
1925 if (!fAttach)
1926 vboxNetFltRelease(pThis, false /* fBusy */);
1927 }
1928 else
1929 {
1930 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to %s the IP stack. rc=%d\n",
1931 fAttach ? "inject into" : "eject from", rc));
1932 }
1933
1934 g_VBoxNetFltSolarisInstance = NULL;
1935 g_VBoxNetFltSolarisStreamType = kUndefined;
1936
1937 if (!fAttach)
1938 vboxNetFltRelease(pThis, false /* fBusy */);
1939 }
1940 else
1941 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to find position. rc=%d rc2=%d\n", rc, rc2));
1942
1943 releasef(Ip4MuxFd);
1944 releasef(ArpMuxFd);
1945 }
1946 else
1947 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get vnode from MuxFd.\n"));
1948 }
1949 else
1950 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2));
1951 }
1952 else
1953 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get MuxFd from MuxId. rc=%d rc2=%d\n"));
1954 }
1955 else
1956 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to get Mux Ids. rc=%d\n", rc));
1957 vboxNetFltSolarisCloseDev(pUdp4VNodeHeld, pUdp4User);
1958 }
1959 else
1960 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: failed to open UDP. rc=%d\n", rc));
1961 }
1962 else
1963 {
1964 /*
1965 * This would happen for interfaces that are not plumbed.
1966 */
1967 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp4: Warning: seems '%s' is unplumbed.\n", pThis->szName));
1968 rc = VINF_SUCCESS;
1969 }
1970
1971 ldi_close(ArpDevHandle, FREAD | FWRITE, kcred);
1972 ldi_close(Ip4DevHandle, FREAD | FWRITE, kcred);
1973
1974 if (RT_SUCCESS(rc))
1975 return rc;
1976
1977 return VERR_INTNET_FLT_IF_FAILED;
1978}
1979
1980
1981/**
1982 * Dynamically attach under IPv6 on the host stack.
1983 *
1984 * @returns VBox status code.
1985 * @param pThis The instance.
1986 * @param fAttach Is this an attach or detach.
1987 */
1988static int vboxNetFltSolarisAttachIp6(PVBOXNETFLTINS pThis, bool fAttach)
1989{
1990 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6 pThis=%p fAttach=%d\n", pThis, fAttach));
1991
1992 /*
1993 * Statuatory Warning: Hackish code ahead.
1994 */
1995 char *pszModName = DEVICE_NAME;
1996
1997 struct lifreq Ip6Interface;
1998 bzero(&Ip6Interface, sizeof(Ip6Interface));
1999 Ip6Interface.lifr_addr.ss_family = AF_INET6;
2000 strncpy(Ip6Interface.lifr_name, pThis->szName, sizeof(Ip6Interface.lifr_name));
2001
2002 struct strmodconf StrMod;
2003 StrMod.mod_name = pszModName;
2004 StrMod.pos = -1; /* this is filled in later. */
2005
2006 int rc;
2007 int rc2;
2008 int ret;
2009 ldi_ident_t DeviceIdent = ldi_ident_from_anon();
2010 ldi_handle_t Ip6DevHandle;
2011 ldi_handle_t Udp6DevHandle;
2012
2013 /*
2014 * Open the IPv6 stream as a layered devices.
2015 */
2016 rc = ldi_open_by_name(IP6_DEV_NAME, FREAD | FWRITE, kcred, &Ip6DevHandle, DeviceIdent);
2017 ldi_ident_release(DeviceIdent);
2018 if (rc)
2019 {
2020 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open the IPv6 stream on '%s'.\n", pThis->szName));
2021 return VERR_INTNET_FLT_IF_FAILED;
2022 }
2023
2024 /*
2025 * Obtain the interface flags from IPv6.
2026 */
2027 rc = vboxNetFltSolarisGetIfFlags(Ip6DevHandle, &Ip6Interface);
2028 if (RT_SUCCESS(rc))
2029 {
2030 /*
2031 * Open the UDP stream. We sort of cheat here and obtain the vnode so that we can perform
2032 * things that are not possible from the layered interface.
2033 */
2034 vnode_t *pUdp6VNode = NULL;
2035 vnode_t *pUdp6VNodeHeld = NULL;
2036 TIUSER *pUdp6User = NULL;
2037 rc = vboxNetFltSolarisOpenDev(UDP6_DEV_NAME, &pUdp6VNode, &pUdp6VNodeHeld, &pUdp6User);
2038 if (RT_SUCCESS(rc))
2039 {
2040 /*
2041 * Get the multiplexor IDs.
2042 */
2043 rc = ldi_ioctl(Ip6DevHandle, SIOCGLIFMUXID, (intptr_t)&Ip6Interface, FKIOCTL, kcred, &ret);
2044 if (!rc)
2045 {
2046 /*
2047 * Get the multiplex file descriptor to the lower streams. Generally this is lost
2048 * once a module is I_PLINK, we need to reobtain it for inserting/removing ourselves from the stack.
2049 */
2050 int Ip6MuxFd;
2051 rc = vboxNetFltSolarisMuxIdToFd(pUdp6VNode, Ip6Interface.lifr_ip_muxid, &Ip6MuxFd);
2052 if (RT_SUCCESS(rc))
2053 {
2054 /*
2055 * We need to I_PUNLINK on these multiplexor IDs before we can start
2056 * operating on the lower stream as insertions are direct operations on the lower stream.
2057 */
2058 int ret;
2059 rc = strioctl(pUdp6VNode, I_PUNLINK, (intptr_t)Ip6Interface.lifr_ip_muxid, 0, K_TO_K, kcred, &ret);
2060 if (!rc)
2061 {
2062 /*
2063 * Obtain the vnode from the useless userland file descriptor.
2064 */
2065 file_t *pIpFile = getf(Ip6MuxFd);
2066 if ( pIpFile
2067 && pIpFile->f_vnode)
2068 {
2069 vnode_t *pIp6VNode = pIpFile->f_vnode;
2070
2071 /*
2072 * Find the position on the host stack for attaching/detaching ourselves.
2073 */
2074 rc = vboxNetFltSolarisDetermineModPos(fAttach, pIp6VNode, &StrMod.pos);
2075 if (RT_SUCCESS(rc))
2076 {
2077 if (!fAttach)
2078 vboxNetFltRetain(pThis, false /* fBusy */);
2079
2080 /*
2081 * Set global data which will be grabbed by ModOpen.
2082 * There is a known (though very unlikely) race here because
2083 * of the inability to pass user data while inserting.
2084 */
2085 g_VBoxNetFltSolarisInstance = pThis;
2086 g_VBoxNetFltSolarisStreamType = kIp6Stream;
2087
2088 /*
2089 * Inject/Eject from the host IPv6 stack.
2090 */
2091 rc = strioctl(pIp6VNode, fAttach ? _I_INSERT : _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K,
2092 kcred, &ret);
2093 if (!rc)
2094 {
2095 g_VBoxNetFltSolarisInstance = NULL;
2096 g_VBoxNetFltSolarisStreamType = kUndefined;
2097
2098 /*
2099 * Our job's not yet over; we need to relink the upper and lower streams
2100 * otherwise we've pretty much screwed up the host interface.
2101 */
2102 rc = vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
2103 if (RT_SUCCESS(rc))
2104 {
2105 /*
2106 * Close the devices ONLY during the return from function case; otherwise
2107 * we end up close twice which is an instant kernel panic.
2108 */
2109 vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
2110 ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
2111 releasef(Ip6MuxFd);
2112
2113 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Success! %s %s@(IPv6:%d) "
2114 "%s interface %s\n", fAttach ? "Injected" : "Ejected", StrMod.mod_name,
2115 StrMod.pos, fAttach ? "to" : "from", pThis->szName));
2116 return VINF_SUCCESS;
2117 }
2118 else
2119 {
2120 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: Relinking failed. Mode=%s rc=%d.\n",
2121 fAttach ? "inject" : "eject", rc));
2122 }
2123
2124 if (fAttach)
2125 strioctl(pIp6VNode, _I_REMOVE, (intptr_t)&StrMod, 0, K_TO_K, kcred, &ret);
2126
2127 vboxNetFltSolarisRelinkIp6(pUdp6VNode, &Ip6Interface, Ip6MuxFd);
2128 }
2129 else
2130 {
2131 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to %s the IP stack. rc=%d\n",
2132 fAttach ? "inject into" : "eject from", rc));
2133 if (!fAttach)
2134 vboxNetFltRelease(pThis, false /* fBusy */);
2135 }
2136 }
2137 else
2138 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to find position. rc=%d rc2=%d\n", rc, rc2));
2139
2140 releasef(Ip6MuxFd);
2141 }
2142 else
2143 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get vnode from MuxFd.\n"));
2144 }
2145 else
2146 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to unlink upper stream rc=%d rc2=%d.\n", rc, rc2));
2147 }
2148 else
2149 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get MuxFd from MuxId. rc=%d rc2=%d\n"));
2150 }
2151 else
2152 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get Mux Ids. rc=%d\n", rc));
2153 vboxNetFltSolarisCloseDev(pUdp6VNodeHeld, pUdp6User);
2154 }
2155 else
2156 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to open UDP. rc=%d\n", rc));
2157 }
2158 else
2159 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAttachIp6: failed to get IPv6 flags.\n", pThis->szName));
2160
2161 ldi_close(Ip6DevHandle, FREAD | FWRITE, kcred);
2162
2163 if (RT_SUCCESS(rc))
2164 return rc;
2165
2166 return VERR_INTNET_FLT_IF_FAILED;
2167}
2168
2169
2170/**
2171 * Wrapper for attaching ourselves to the interface.
2172 *
2173 * @returns VBox status code.
2174 * @param pThis The instance.
2175 */
2176static int vboxNetFltSolarisAttachToInterface(PVBOXNETFLTINS pThis)
2177{
2178 int rc = vboxNetFltSolarisOpenStream(pThis);
2179 if (RT_SUCCESS(rc))
2180 {
2181 rc = vboxNetFltSolarisAttachIp4(pThis, true /* fAttach */);
2182 if (RT_SUCCESS(rc))
2183 {
2184 vboxNetFltSolarisAttachIp6(pThis, true /* fAttach */);
2185 /* Ignore Ipv6 binding errors as it's optional. */
2186
2187 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, false);
2188 }
2189 else
2190 vboxNetFltSolarisCloseStream(pThis);
2191 }
2192 else
2193 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface vboxNetFltSolarisOpenStream failed rc=%Rrc\n", rc));
2194
2195 return rc;
2196}
2197
2198
2199/**
2200 * Wrapper for detaching ourselves from the interface.
2201 *
2202 * @returns VBox status code.
2203 * @param pThis The instance.
2204 * @remarks Owns the globals mutex, so re-requesting it anytime during this phase
2205 * would panic the system (e.g. in vboxNetFltSolarisFindInstance).
2206 */
2207static int vboxNetFltSolarisDetachFromInterface(PVBOXNETFLTINS pThis)
2208{
2209 LogFlow((DEVICE_NAME ":vboxNetFltSolarisDetachFromInterface pThis=%p\n", pThis));
2210
2211 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
2212 vboxNetFltSolarisCloseStream(pThis);
2213 int rc = vboxNetFltSolarisAttachIp4(pThis, false /* fAttach */);
2214 if (pThis->u.s.pvIp6Stream)
2215 rc = vboxNetFltSolarisAttachIp6(pThis, false /* fAttach */);
2216
2217 return rc;
2218}
2219
2220
2221/**
2222 * Create a solaris message block from the SG list.
2223 *
2224 * @returns Solaris message block.
2225 * @param pThis The instance.
2226 * @param pSG Pointer to the scatter-gather list.
2227 */
2228static mblk_t *vboxNetFltSolarisMBlkFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
2229{
2230 LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG pThis=%p pSG=%p\n"));
2231
2232 mblk_t *pMsg = allocb(pSG->cbTotal, BPRI_MED);
2233 if (RT_UNLIKELY(!pMsg))
2234 {
2235 LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed to alloc %d bytes for mblk_t.\n", pSG->cbTotal));
2236 return NULL;
2237 }
2238
2239 /*
2240 * Single buffer copy. Maybe later explore the
2241 * need/possibility for using a mblk_t chain rather.
2242 */
2243 for (unsigned i = 0; i < pSG->cSegsUsed; i++)
2244 {
2245 if (pSG->aSegs[i].pv)
2246 {
2247 bcopy(pSG->aSegs[i].pv, pMsg->b_wptr, pSG->aSegs[i].cb);
2248 pMsg->b_wptr += pSG->aSegs[i].cb;
2249 }
2250 }
2251 DB_TYPE(pMsg) = M_DATA;
2252 return pMsg;
2253}
2254
2255
2256/**
2257 * Calculate the number of segments required for this message block.
2258 *
2259 * @returns Number of segments.
2260 * @param pThis The instance
2261 * @param pMsg Pointer to the data message.
2262 */
2263static unsigned vboxNetFltSolarisMBlkCalcSGSegs(PVBOXNETFLTINS pThis, mblk_t *pMsg)
2264{
2265 unsigned cSegs = 0;
2266 for (mblk_t *pCur = pMsg; pCur; pCur = pCur->b_cont)
2267 if (MBLKL(pCur))
2268 cSegs++;
2269
2270#ifdef PADD_RUNT_FRAMES_FROM_HOST
2271 if (msgdsize(pMsg) < 60)
2272 cSegs++;
2273#endif
2274
2275 NOREF(pThis);
2276 return RT_MAX(cSegs, 1);
2277}
2278
2279
2280/**
2281 * Initializes an SG list from the given message block.
2282 *
2283 * @returns VBox status code.
2284 * @param pThis The instance.
2285 * @param pMsg Pointer to the data message.
2286 The caller must ensure it's not a control message block.
2287 * @param pSG Pointer to the SG.
2288 * @param cSegs Number of segments in the SG.
2289 * This should match the number in the message block exactly!
2290 * @param fSrc The source of the message.
2291 */
2292static int vboxNetFltSolarisMBlkToSG(PVBOXNETFLTINS pThis, mblk_t *pMsg, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
2293{
2294 LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pThis=%p pMsg=%p pSG=%p cSegs=%d\n", pThis, pMsg, pSG, cSegs));
2295
2296 pSG->pvOwnerData = NULL;
2297 pSG->pvUserData = NULL;
2298 pSG->pvUserData2 = NULL;
2299 pSG->cUsers = 1;
2300 pSG->cbTotal = 0;
2301 pSG->fFlags = INTNETSG_FLAGS_TEMP;
2302 pSG->cSegsAlloc = cSegs;
2303
2304 /*
2305 * Convert the message block to segments.
2306 */
2307 mblk_t *pCur = pMsg;
2308 unsigned iSeg = 0;
2309 while (pCur)
2310 {
2311 size_t cbSeg = MBLKL(pCur);
2312 if (cbSeg)
2313 {
2314 void *pvSeg = pCur->b_rptr;
2315 pSG->aSegs[iSeg].pv = pvSeg;
2316 pSG->aSegs[iSeg].cb = cbSeg;
2317 pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
2318 pSG->cbTotal += cbSeg;
2319 iSeg++;
2320 }
2321 pCur = pCur->b_cont;
2322 }
2323 pSG->cSegsUsed = iSeg;
2324
2325#ifdef PADD_RUNT_FRAMES_FROM_HOST
2326 if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
2327 {
2328 LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG pulling up to length.\n"));
2329
2330 static uint8_t const s_abZero[128] = {0};
2331 pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
2332 pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
2333 pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
2334 pSG->cbTotal = 60;
2335 pSG->cSegsUsed++;
2336 }
2337#endif
2338
2339 LogFlow((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG iSeg=%d pSG->cbTotal=%d msgdsize=%d\n", iSeg, pSG->cbTotal, msgdsize(pMsg)));
2340 return VINF_SUCCESS;
2341}
2342
2343
2344/**
2345 * Converts raw mode M_DATA messages to M_PROTO DL_UNITDATA_IND format.
2346 *
2347 * @returns VBox status code.
2348 * @param pMsg Pointer to the raw message.
2349 * @param pDlpiMsg Where to store the M_PROTO message.
2350 *
2351 * @remarks The original raw message would be no longer valid and will be
2352 * linked as part of the new DLPI message. Callers must take care
2353 * not to use the raw message if this routine is successful.
2354 */
2355static int vboxNetFltSolarisRawToUnitData(mblk_t *pMsg, mblk_t **ppDlpiMsg)
2356{
2357 LogFlow((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData pMsg=%p\n", pMsg));
2358
2359 if (DB_TYPE(pMsg) != M_DATA)
2360 return VERR_NO_MEMORY;
2361
2362 size_t cbMsg = sizeof(dl_unitdata_ind_t) + 2 * sizeof(vboxnetflt_dladdr_t);
2363 mblk_t *pDlpiMsg = allocb(cbMsg, BPRI_MED);
2364 if (RT_UNLIKELY(!pDlpiMsg))
2365 return VERR_NO_MEMORY;
2366
2367 DB_TYPE(pDlpiMsg) = M_PROTO;
2368 dl_unitdata_ind_t *pDlpiData = (dl_unitdata_ind_t *)pDlpiMsg->b_rptr;
2369 pDlpiData->dl_primitive = DL_UNITDATA_IND;
2370 pDlpiData->dl_dest_addr_length = VBOXNETFLT_DLADDRL;
2371 pDlpiData->dl_dest_addr_offset = sizeof(dl_unitdata_ind_t);
2372 pDlpiData->dl_src_addr_length = VBOXNETFLT_DLADDRL;
2373 pDlpiData->dl_src_addr_offset = VBOXNETFLT_DLADDRL + sizeof(dl_unitdata_ind_t);
2374
2375 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
2376
2377 vboxnetflt_dladdr_t *pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_dest_addr_offset);
2378 pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
2379 bcopy(&pEthHdr->DstMac, &pDlAddr->Mac, sizeof(RTMAC));
2380
2381 pDlAddr = (vboxnetflt_dladdr_t *)(pDlpiMsg->b_rptr + pDlpiData->dl_src_addr_offset);
2382 pDlAddr->SAP = RT_BE2H_U16(pEthHdr->EtherType);
2383 bcopy(&pEthHdr->SrcMac, &pDlAddr->Mac, sizeof(RTMAC));
2384
2385 pDlpiMsg->b_wptr = pDlpiMsg->b_rptr + cbMsg;
2386
2387 /* Make the message point to the protocol header */
2388 pMsg->b_rptr += sizeof(RTNETETHERHDR);
2389
2390 pDlpiMsg->b_cont = pMsg;
2391 *ppDlpiMsg = pDlpiMsg;
2392 return VINF_SUCCESS;
2393}
2394
2395
2396/**
2397 * Converts DLPI M_PROTO messages to the raw mode M_DATA format.
2398 *
2399 * @returns VBox status code.
2400 * @param pMsg Pointer to the M_PROTO message.
2401 * @param ppRawMsg Where to store the converted message.
2402 *
2403 * @remarks If successful, the original pMsg is no longer valid, it will be deleted.
2404 * Callers must take care not to continue to use pMsg after a successful
2405 * call to this conversion routine.
2406 */
2407static int vboxNetFltSolarisUnitDataToRaw(PVBOXNETFLTINS pThis, mblk_t *pMsg, mblk_t **ppRawMsg)
2408{
2409 LogFlow((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw pMsg=%p\n", pMsg));
2410
2411 if ( !pMsg->b_cont
2412 || DB_TYPE(pMsg) != M_PROTO)
2413 {
2414 LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw invalid input message.\n"));
2415 return VERR_NET_PROTOCOL_ERROR;
2416 }
2417
2418 /*
2419 * Upstream consumers send/receive packets in the fast path mode.
2420 * We of course need to convert them into raw ethernet frames.
2421 */
2422 RTNETETHERHDR EthHdr;
2423 union DL_primitives *pPrim = (union DL_primitives *)pMsg->b_rptr;
2424 switch (pPrim->dl_primitive)
2425 {
2426 case DL_UNITDATA_IND:
2427 {
2428 /*
2429 * Receive side.
2430 */
2431 dl_unitdata_ind_t *pDlpiMsg = (dl_unitdata_ind_t *)pMsg->b_rptr;
2432 bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
2433 bcopy(pMsg->b_rptr + pDlpiMsg->dl_src_addr_offset, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
2434
2435 vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
2436 EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
2437
2438 break;
2439 }
2440
2441 case DL_UNITDATA_REQ:
2442 {
2443 /*
2444 * Send side.
2445 */
2446 dl_unitdata_req_t *pDlpiMsg = (dl_unitdata_req_t *)pMsg->b_rptr;
2447
2448 bcopy(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset, &EthHdr.DstMac, sizeof(EthHdr.DstMac));
2449 bcopy(&pThis->u.s.Mac, &EthHdr.SrcMac, sizeof(EthHdr.SrcMac));
2450
2451 vboxnetflt_dladdr_t *pDLSapAddr = (vboxnetflt_dladdr_t *)(pMsg->b_rptr + pDlpiMsg->dl_dest_addr_offset);
2452 EthHdr.EtherType = RT_H2BE_U16(pDLSapAddr->SAP);
2453
2454 break;
2455 }
2456
2457 default:
2458 {
2459 LogRel((DEVICE_NAME ":vboxNetFltSolarisUnitDataToRaw Unknown M_PROTO. This shouldn't be happening!!"));
2460 return VERR_NET_PROTOCOL_ERROR;
2461 }
2462 }
2463
2464 /*
2465 * Let us just link it as a mblk_t chain rather than re-copy the entire message.
2466 * The vboxNetFltSolarisMBlkToSG function will handle chained mblk_t's.
2467 */
2468 size_t cbLen = sizeof(EthHdr);
2469 mblk_t *pEtherMsg = allocb(cbLen, BPRI_MED);
2470 if (RT_UNLIKELY(!pEtherMsg))
2471 return VERR_NO_MEMORY;
2472
2473 DB_TYPE(pEtherMsg) = M_DATA;
2474 bcopy(&EthHdr, pEtherMsg->b_wptr, sizeof(EthHdr));
2475 pEtherMsg->b_wptr += cbLen;
2476
2477 pEtherMsg->b_cont = pMsg->b_cont;
2478
2479 /*
2480 * Change the chained blocks to type M_DATA.
2481 */
2482 for (mblk_t *pTmp = pEtherMsg->b_cont; pTmp; pTmp = pTmp->b_cont)
2483 DB_TYPE(pTmp) = M_DATA;
2484
2485 pMsg->b_cont = NULL;
2486 freemsg(pMsg);
2487
2488 *ppRawMsg = pEtherMsg;
2489 return VINF_SUCCESS;
2490}
2491
2492
2493/**
2494 * Initializes a packet identifier.
2495 *
2496 * @param pTag Pointer to the packed identifier.
2497 * @param pMsg Pointer to the message to be identified.
2498 *
2499 * @remarks Warning!!! This function assumes 'pMsg' is an unchained message.
2500 */
2501static inline void vboxNetFltSolarisInitPacketId(PVBOXNETFLTPACKETID pTag, mblk_t *pMsg)
2502{
2503 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
2504 size_t cbMsg = MBLKL(pMsg);
2505
2506 pTag->cbPacket = cbMsg;
2507 pTag->Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
2508 bcopy(&pEthHdr->SrcMac, &pTag->SrcMac, sizeof(RTMAC));
2509 bcopy(&pEthHdr->DstMac, &pTag->DstMac, sizeof(RTMAC));
2510}
2511
2512
2513/**
2514 * Queues a packet for loopback elimination.
2515 *
2516 * @returns VBox status code.
2517 * @param pThis The instance.
2518 * @param pPromiscStream Pointer to the promiscuous stream.
2519 * @param pMsg Pointer to the message.
2520 */
2521static int vboxNetFltSolarisQueueLoopback(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
2522{
2523 Assert(pThis);
2524 Assert(pMsg);
2525 Assert(DB_TYPE(pMsg) == M_DATA);
2526 Assert(pPromiscStream);
2527
2528 LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback pThis=%p pPromiscStream=%p pMsg=%p\n", pThis, pPromiscStream, pMsg));
2529
2530 if (RT_UNLIKELY(pMsg->b_cont))
2531 {
2532 /*
2533 * We don't currently make chained messages in on Xmit
2534 * so this only needs to be supported when we do that.
2535 */
2536 return VERR_NOT_SUPPORTED;
2537 }
2538
2539 size_t cbMsg = MBLKL(pMsg);
2540 if (RT_UNLIKELY(cbMsg < sizeof(RTNETETHERHDR)))
2541 return VERR_NET_MSG_SIZE;
2542
2543 int rc = RTSemFastMutexRequest(pThis->u.s.hFastMtx);
2544 AssertRCReturn(rc, rc);
2545
2546 PVBOXNETFLTPACKETID pCur = NULL;
2547 if (pPromiscStream->cLoopback < VBOXNETFLT_LOOPBACK_SIZE
2548 || ( pPromiscStream->pHead
2549 && pPromiscStream->pHead->cbPacket == 0))
2550 {
2551 do
2552 {
2553 if (!pPromiscStream->pHead)
2554 {
2555 pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
2556 if (RT_UNLIKELY(!pCur))
2557 {
2558 rc = VERR_NO_MEMORY;
2559 break;
2560 }
2561
2562 vboxNetFltSolarisInitPacketId(pCur, pMsg);
2563
2564 pCur->pNext = NULL;
2565 pPromiscStream->pHead = pCur;
2566 pPromiscStream->pTail = pCur;
2567 pPromiscStream->cLoopback++;
2568
2569 LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback initialized head. checksum=%u.\n",
2570 pPromiscStream->pHead->Checksum));
2571 break;
2572 }
2573 else if ( pPromiscStream->pHead
2574 && pPromiscStream->pHead->cbPacket == 0)
2575 {
2576 pCur = pPromiscStream->pHead;
2577 vboxNetFltSolarisInitPacketId(pCur, pMsg);
2578
2579 LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback re-used head checksum=%u cLoopback=%d.\n",
2580 pCur->Checksum, pPromiscStream->cLoopback));
2581 break;
2582 }
2583 else
2584 {
2585 pCur = RTMemAlloc(sizeof(VBOXNETFLTPACKETID));
2586 if (RT_UNLIKELY(!pCur))
2587 {
2588 rc = VERR_NO_MEMORY;
2589 break;
2590 }
2591
2592 vboxNetFltSolarisInitPacketId(pCur, pMsg);
2593
2594 pCur->pNext = pPromiscStream->pHead;
2595 pPromiscStream->pHead = pCur;
2596 pPromiscStream->cLoopback++;
2597
2598 LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback added head checksum=%u cLoopback=%d.\n", pCur->Checksum,
2599 pPromiscStream->cLoopback));
2600 break;
2601 }
2602 } while (0);
2603 }
2604 else
2605 {
2606 /*
2607 * Maximum loopback queue size reached. Re-use tail as head.
2608 */
2609 Assert(pPromiscStream->pHead);
2610 Assert(pPromiscStream->pTail);
2611
2612 /*
2613 * Find tail's previous item.
2614 */
2615 PVBOXNETFLTPACKETID pPrev = NULL;
2616 pCur = pPromiscStream->pHead;
2617
2618 /** @todo consider if this is worth switching to a double linked list... */
2619 while (pCur != pPromiscStream->pTail)
2620 {
2621 pPrev = pCur;
2622 pCur = pCur->pNext;
2623 }
2624
2625 pPromiscStream->pTail = pPrev;
2626 pPromiscStream->pTail->pNext = NULL;
2627 pCur->pNext = pPromiscStream->pHead;
2628 pPromiscStream->pHead = pCur;
2629
2630 vboxNetFltSolarisInitPacketId(pCur, pMsg);
2631 LogFlow((DEVICE_NAME ":vboxNetFltSolarisQueueLoopback recycled tail!! checksum=%u cLoopback=%d\n", pCur->Checksum,
2632 pPromiscStream->cLoopback));
2633 }
2634
2635 RTSemFastMutexRelease(pThis->u.s.hFastMtx);
2636
2637 return rc;
2638}
2639
2640
2641/**
2642 * Checks if the packet is enqueued for loopback as our own packet.
2643 *
2644 * @returns If it's our packet, returns true after dequeuing it, otherwise false.
2645 * @param pThis The instance.
2646 * @param pPromiscStream Pointer to the promiscuous stream.
2647 * @param pMsg Pointer to the message.
2648 */
2649static bool vboxNetFltSolarisIsOurMBlk(PVBOXNETFLTINS pThis, vboxnetflt_promisc_stream_t *pPromiscStream, mblk_t *pMsg)
2650{
2651 Assert(pThis);
2652 Assert(pPromiscStream);
2653 Assert(pMsg);
2654 Assert(DB_TYPE(pMsg) == M_DATA);
2655
2656 LogFlow((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk pThis=%p pMsg=%p\n", pThis, pMsg));
2657
2658 if (pMsg->b_cont)
2659 {
2660 /** Handle this when Xmit makes chained messages */
2661 return false;
2662 }
2663
2664 size_t cbMsg = MBLKL(pMsg);
2665 if (cbMsg < sizeof(RTNETETHERHDR))
2666 return false;
2667
2668 int rc = RTSemFastMutexRequest(pThis->u.s.hFastMtx);
2669 AssertRCReturn(rc, rc);
2670
2671 PVBOXNETFLTPACKETID pPrev = NULL;
2672 PVBOXNETFLTPACKETID pCur = pPromiscStream->pHead;
2673 bool fIsOurPacket = false;
2674 while (pCur)
2675 {
2676 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
2677 if ( pCur->cbPacket != cbMsg
2678 || pCur->SrcMac.au8[0] != pEthHdr->SrcMac.au8[0]
2679 || pCur->SrcMac.au8[1] != pEthHdr->SrcMac.au8[1]
2680 || pCur->SrcMac.au8[2] != pEthHdr->SrcMac.au8[2]
2681 || pCur->SrcMac.au8[3] != pEthHdr->SrcMac.au8[3]
2682 || pCur->SrcMac.au8[4] != pEthHdr->SrcMac.au8[4]
2683 || pCur->SrcMac.au8[5] != pEthHdr->SrcMac.au8[5]
2684 || pCur->DstMac.au8[0] != pEthHdr->DstMac.au8[0]
2685 || pCur->DstMac.au8[1] != pEthHdr->DstMac.au8[1]
2686 || pCur->DstMac.au8[2] != pEthHdr->DstMac.au8[2]
2687 || pCur->DstMac.au8[3] != pEthHdr->DstMac.au8[3]
2688 || pCur->DstMac.au8[4] != pEthHdr->DstMac.au8[4]
2689 || pCur->DstMac.au8[5] != pEthHdr->DstMac.au8[5])
2690 {
2691 pPrev = pCur;
2692 pCur = pCur->pNext;
2693 continue;
2694 }
2695
2696 uint16_t Checksum = RTCrc32(pMsg->b_rptr, cbMsg);
2697 if (pCur->Checksum != Checksum)
2698 {
2699 pPrev = pCur;
2700 pCur = pCur->pNext;
2701 continue;
2702 }
2703
2704 /*
2705 * Yes, it really is our own packet, mark it as handled
2706 * and move it as a "free slot" to the head and return success.
2707 */
2708 pCur->cbPacket = 0;
2709 if (pPrev)
2710 {
2711 if (!pCur->pNext)
2712 pPromiscStream->pTail = pPrev;
2713
2714 pPrev->pNext = pCur->pNext;
2715 pCur->pNext = pPromiscStream->pHead;
2716 pPromiscStream->pHead = pCur;
2717 }
2718 fIsOurPacket = true;
2719
2720 LogFlow((DEVICE_NAME ":vboxNetFltSolarisIsOurMBlk found packet %p Checksum=%u cLoopback=%d\n", pMsg, Checksum,
2721 pPromiscStream->cLoopback));
2722 break;
2723 }
2724
2725 RTSemFastMutexRelease(pThis->u.s.hFastMtx);
2726 return fIsOurPacket;
2727}
2728
2729
2730/**
2731 * Worker for routing messages from the wire or from the host.
2732 *
2733 * @returns VBox status code.
2734 * @param pThis The instance.
2735 * @param pStream Pointer to the stream.
2736 * @param pQueue Pointer to the read queue.
2737 * @param pOrigMsg Pointer to the message.
2738 */
2739static int vboxNetFltSolarisRecv(PVBOXNETFLTINS pThis, vboxnetflt_stream_t *pStream, queue_t *pQueue, mblk_t *pMsg)
2740{
2741 LogFlow((DEVICE_NAME ":vboxNetFltSolarisRecv pThis=%p pMsg=%p\n", pThis, pMsg));
2742
2743 AssertCompile(sizeof(struct ether_header) == sizeof(RTNETETHERHDR));
2744 Assert(pStream->Type == kPromiscStream);
2745
2746 vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream);
2747 if (RT_UNLIKELY(!pPromiscStream))
2748 {
2749 LogRel((DEVICE_NAME ":Promiscuous stream missing!! Failing to receive packet.\n"));
2750 return VERR_INVALID_POINTER;
2751 }
2752
2753 /*
2754 * Don't loopback packets we transmit to the wire.
2755 */
2756 /** @todo maybe we need not check for loopback for INTNETTRUNKDIR_HOST case? */
2757 if (vboxNetFltSolarisIsOurMBlk(pThis, pPromiscStream, pMsg))
2758 {
2759 LogFlow((DEVICE_NAME ":Avoiding packet loopback.\n"));
2760 return VINF_SUCCESS;
2761 }
2762
2763 /*
2764 * Figure out the source of the packet based on the source Mac address.
2765 */
2766 uint32_t fSrc = INTNETTRUNKDIR_WIRE;
2767 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
2768 if (vboxNetFltPortOsIsHostMac(pThis, &pEthHdr->SrcMac))
2769 fSrc = INTNETTRUNKDIR_HOST;
2770
2771 /*
2772 * Afaik; we no longer need to worry about incorrect checksums because we now use
2773 * a dedicated stream and don't intercept packets under IP/ARP which might be doing
2774 * checksum offloading.
2775 */
2776#if 0
2777 if (fSrc & INTNETTRUNKDIR_HOST)
2778 {
2779 mblk_t *pCorrectedMsg = vboxNetFltSolarisFixChecksums(pMsg);
2780 if (pCorrectedMsg)
2781 pMsg = pCorrectedMsg;
2782 }
2783 vboxNetFltSolarisAnalyzeMBlk(pMsg);
2784#endif
2785
2786 /*
2787 * Route all received packets into the internal network.
2788 */
2789 unsigned cSegs = vboxNetFltSolarisMBlkCalcSGSegs(pThis, pMsg);
2790 PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
2791 int rc = vboxNetFltSolarisMBlkToSG(pThis, pMsg, pSG, cSegs, fSrc);
2792 if (RT_SUCCESS(rc))
2793 pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, fSrc);
2794 else
2795 LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkToSG failed. rc=%d\n", rc));
2796
2797 return VINF_SUCCESS;
2798}
2799
2800
2801/**
2802 * Finalize the message to be fed into the internal network.
2803 * Verifies and tries to fix checksums for TCP, UDP and IP.
2804 *
2805 * @returns Corrected message or NULL if no change was required.
2806 * @param pMsg Pointer to the message block.
2807 * This must not be DLPI linked messages, must be M_DATA.
2808 *
2809 * @remarks If this function returns a checksum adjusted message, the
2810 * passed in input message has been freed and should not be
2811 * referenced anymore by the caller.
2812 */
2813static mblk_t *vboxNetFltSolarisFixChecksums(mblk_t *pMsg)
2814{
2815 LogFlow((DEVICE_NAME ":vboxNetFltSolarisFixChecksums pMsg=%p\n"));
2816
2817 Assert(DB_TYPE(pMsg) == M_DATA);
2818
2819 if (MBLKL(pMsg) < sizeof(RTNETETHERHDR))
2820 {
2821 LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums Packet shorter than ethernet header size!\n"));
2822 return NULL;
2823 }
2824
2825 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pMsg->b_rptr;
2826 if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
2827 {
2828 /*
2829 * Check if we have a complete packet or being fed a chain.
2830 */
2831 size_t cbIpPacket = 0;
2832 mblk_t *pFullMsg = NULL;
2833 if (pMsg->b_cont)
2834 {
2835 LogFlow((DEVICE_NAME ":Chained mblk_t.\n"));
2836
2837 /*
2838 * Handle chain by making a packet copy to verify if the IP checksum is correct.
2839 * Contributions to calculating IP checksums from a chained message block with
2840 * odd/non-pulled up sizes are welcome.
2841 */
2842 size_t cbFullMsg = msgdsize(pMsg);
2843 mblk_t *pFullMsg = allocb(cbFullMsg, BPRI_MED);
2844 LogFlow((DEVICE_NAME ":msgdsize returns %d\n", cbFullMsg));
2845 if (RT_UNLIKELY(!pFullMsg))
2846 {
2847 LogRel((DEVICE_NAME ":vboxNetFltSolarisFixChecksums failed to alloc new message of %d bytes.\n", cbFullMsg));
2848 return NULL;
2849 }
2850
2851 for (mblk_t *pTmp = pMsg; pTmp; pTmp = pTmp->b_cont)
2852 {
2853 if (DB_TYPE(pTmp) == M_DATA)
2854 {
2855 bcopy(pTmp->b_rptr, pFullMsg->b_wptr, MBLKL(pTmp));
2856 pFullMsg->b_wptr += MBLKL(pTmp);
2857 }
2858 }
2859
2860 DB_TYPE(pFullMsg) = M_DATA;
2861 pEthHdr = (PRTNETETHERHDR)pFullMsg->b_rptr;
2862 cbIpPacket = MBLKL(pFullMsg) - sizeof(RTNETETHERHDR);
2863 }
2864 else
2865 cbIpPacket = MBLKL(pMsg) - sizeof(RTNETETHERHDR);
2866
2867 /*
2868 * Check if the IP checksum is valid.
2869 */
2870 uint8_t *pbProtocol = (uint8_t *)(pEthHdr + 1);
2871 PRTNETIPV4 pIpHdr = (PRTNETIPV4)pbProtocol;
2872 size_t cbPayload = cbIpPacket - (pIpHdr->ip_hl << 2);
2873 bool fChecksumAdjusted = false;
2874 if (RTNetIPv4IsHdrValid(pIpHdr, cbPayload, cbPayload))
2875 {
2876 pbProtocol += (pIpHdr->ip_hl << 2);
2877
2878 /*
2879 * Fix up TCP/UDP and IP checksums if they're incomplete/invalid.
2880 */
2881 if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
2882 {
2883 PRTNETTCP pTcpHdr = (PRTNETTCP)pbProtocol;
2884 uint16_t TcpChecksum = RTNetIPv4TCPChecksum(pIpHdr, pTcpHdr, NULL);
2885 if (pTcpHdr->th_sum != TcpChecksum)
2886 {
2887 pTcpHdr->th_sum = TcpChecksum;
2888 fChecksumAdjusted = true;
2889 LogFlow((DEVICE_NAME ":fixed TCP checksum.\n"));
2890 }
2891 }
2892 else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
2893 {
2894 PRTNETUDP pUdpHdr = (PRTNETUDP)pbProtocol;
2895 uint16_t UdpChecksum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
2896
2897 if (pUdpHdr->uh_sum != UdpChecksum)
2898 {
2899 pUdpHdr->uh_sum = UdpChecksum;
2900 fChecksumAdjusted = true;
2901 LogFlow((DEVICE_NAME ":Fixed UDP checksum."));
2902 }
2903 }
2904 }
2905
2906 if (fChecksumAdjusted)
2907 {
2908 /*
2909 * If we made a copy and the checksum is corrected on the copy,
2910 * free the original, return the checksum fixed copy.
2911 */
2912 if (pFullMsg)
2913 {
2914 freemsg(pMsg);
2915 return pFullMsg;
2916 }
2917
2918 return pMsg;
2919 }
2920
2921 /*
2922 * If we made a copy and the checksum is NOT corrected, free the copy,
2923 * and return NULL.
2924 */
2925 if (pFullMsg)
2926 freemsg(pFullMsg);
2927
2928 return NULL;
2929 }
2930
2931 return NULL;
2932}
2933
2934
2935/**
2936 * Simple packet dump, used for internal debugging.
2937 *
2938 * @param pMsg Pointer to the message to analyze and dump.
2939 */
2940static void vboxNetFltSolarisAnalyzeMBlk(mblk_t *pMsg)
2941{
2942 LogFlow((DEVICE_NAME ":vboxNetFltSolarisAnalyzeMBlk pMsg=%p\n", pMsg));
2943
2944 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
2945 uint8_t *pb = pMsg->b_rptr;
2946 if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
2947 {
2948 PRTNETIPV4 pIpHdr = (PRTNETIPV4)(pEthHdr + 1);
2949 size_t cbLen = MBLKL(pMsg) - sizeof(*pEthHdr);
2950 if (!pMsg->b_cont)
2951 {
2952 if (pIpHdr->ip_p == RTNETIPV4_PROT_ICMP)
2953 LogFlow((DEVICE_NAME ":ICMP D=%.6Rhxs S=%.6Rhxs T=%04x\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
2954 else if (pIpHdr->ip_p == RTNETIPV4_PROT_TCP)
2955 LogFlow((DEVICE_NAME ":TCP D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
2956 else if (pIpHdr->ip_p == RTNETIPV4_PROT_UDP)
2957 {
2958 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
2959 if ( RT_BE2H_U16(pUdpHdr->uh_sport) == 67
2960 && RT_BE2H_U16(pUdpHdr->uh_dport) == 68)
2961 {
2962 LogRel((DEVICE_NAME ":UDP bootp ack D=%.6Rhxs S=%.6Rhxs UDP_CheckSum=%04x Computex=%04x\n", pb, pb + 6,
2963 RT_BE2H_U16(pUdpHdr->uh_sum), RT_BE2H_U16(RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1))));
2964 }
2965 }
2966 }
2967 else
2968 {
2969 LogFlow((DEVICE_NAME ":Chained IP packet. Skipping validity check.\n"));
2970 }
2971 }
2972 else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_VLAN))
2973 {
2974 typedef struct VLANHEADER
2975 {
2976 int Pcp:3;
2977 int Cfi:1;
2978 int Vid:12;
2979 } VLANHEADER;
2980
2981 VLANHEADER *pVlanHdr = (VLANHEADER *)(pMsg->b_rptr + sizeof(RTNETETHERHDR));
2982 LogFlow((DEVICE_NAME ":VLAN Pcp=%d Cfi=%d Id=%d\n", pVlanHdr->Pcp, pVlanHdr->Cfi, pVlanHdr->Vid >> 4));
2983 LogFlow((DEVICE_NAME "%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr));
2984 }
2985 else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
2986 {
2987 PRTNETARPHDR pArpHdr = (PRTNETARPHDR)(pEthHdr + 1);
2988 LogFlow((DEVICE_NAME ":ARP Op=%d\n", pArpHdr->ar_oper));
2989 }
2990 else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
2991 {
2992 LogFlow((DEVICE_NAME ":IPv6 D=%.6Rhxs S=%.6Rhxs\n", pb, pb + 6));
2993 }
2994 else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_1)
2995 || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_2)
2996 || pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPX_3))
2997 {
2998 LogFlow((DEVICE_NAME ":IPX packet.\n"));
2999 }
3000 else
3001 {
3002 LogFlow((DEVICE_NAME ":Unknown EtherType=%x D=%.6Rhxs S=%.6Rhxs\n", RT_H2BE_U16(pEthHdr->EtherType), &pEthHdr->DstMac,
3003 &pEthHdr->SrcMac));
3004 /* LogFlow((DEVICE_NAME ":%.*Rhxd\n", MBLKL(pMsg), pMsg->b_rptr)); */
3005 }
3006}
3007
3008
3009/* -=-=-=-=-=- Common Hooks -=-=-=-=-=- */
3010bool vboxNetFltPortOsIsPromiscuous(PVBOXNETFLTINS pThis)
3011{
3012 /*
3013 * There is no easy way of obtaining the global host side promiscuous counter.
3014 * Currently we just return false.
3015 */
3016 return false;
3017}
3018
3019
3020void vboxNetFltPortOsGetMacAddress(PVBOXNETFLTINS pThis, PRTMAC pMac)
3021{
3022 LogFlow((DEVICE_NAME ":vboxNetFltPortOsGetMacAddress pThis=%p\n", pThis));
3023 *pMac = pThis->u.s.Mac;
3024}
3025
3026
3027bool vboxNetFltPortOsIsHostMac(PVBOXNETFLTINS pThis, PCRTMAC pMac)
3028{
3029 /*
3030 * MAC address change acknowledgements are intercepted on the read side
3031 * hence theoritically we are always update to date with any changes.
3032 */
3033 return pThis->u.s.Mac.au16[0] == pMac->au16[0]
3034 && pThis->u.s.Mac.au16[1] == pMac->au16[1]
3035 && pThis->u.s.Mac.au16[2] == pMac->au16[2];
3036}
3037
3038
3039void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
3040{
3041 LogFlow((DEVICE_NAME ":vboxNetFltPortOsSetActive pThis=%p fActive=%d\n", pThis, fActive));
3042
3043 /*
3044 * Enable/disable promiscuous mode.
3045 */
3046 vboxnetflt_stream_t *pStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream);
3047 if (pStream)
3048 {
3049 if (pStream->pReadQueue)
3050 {
3051 int rc = vboxNetFltSolarisPromiscReq(pStream->pReadQueue, fActive);
3052 if (RT_FAILURE(rc))
3053 LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive failed to request promiscuous mode! rc=%d\n", rc));
3054 }
3055 else
3056 LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive queue not found!\n"));
3057 }
3058 else
3059 LogRel((DEVICE_NAME ":vboxNetFltPortOsSetActive stream not found!\n"));
3060}
3061
3062
3063int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
3064{
3065 LogFlow((DEVICE_NAME ":vboxNetFltOsDisconnectIt pThis=%p\n", pThis));
3066
3067 vboxNetFltSolarisDetachFromInterface(pThis);
3068
3069 if (pThis->u.s.hFastMtx != NIL_RTSEMFASTMUTEX)
3070 {
3071 RTSemFastMutexDestroy(pThis->u.s.hFastMtx);
3072 pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX;
3073 }
3074 return VINF_SUCCESS;
3075}
3076
3077
3078int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
3079{
3080 /* Nothing to do here. */
3081 return VINF_SUCCESS;
3082}
3083
3084
3085void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
3086{
3087 LogFlow((DEVICE_NAME ":vboxNetFltOsDeleteInstance pThis=%p\n", pThis));
3088 /* Nothing to do here. */
3089}
3090
3091
3092int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis)
3093{
3094 LogFlow((DEVICE_NAME ":vboxNetFltOsInitInstance pThis=%p\n"));
3095
3096 /*
3097 * Mutex used for loopback lockouts.
3098 */
3099 int rc = RTSemFastMutexCreate(&pThis->u.s.hFastMtx);
3100 if (RT_SUCCESS(rc))
3101 {
3102 rc = vboxNetFltSolarisAttachToInterface(pThis);
3103 if (RT_SUCCESS(rc))
3104 return rc;
3105
3106 LogRel((DEVICE_NAME ":vboxNetFltSolarisAttachToInterface failed. rc=%Rrc\n", rc));
3107 RTSemFastMutexDestroy(pThis->u.s.hFastMtx);
3108 pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX;
3109 }
3110 else
3111 LogRel((DEVICE_NAME ":vboxNetFltOsInitInstance failed to create mutex. rc=%Rrc\n", rc));
3112
3113 return rc;
3114}
3115
3116
3117int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
3118{
3119 /*
3120 * Init. the solaris specific data.
3121 */
3122 pThis->u.s.pvIp4Stream = NULL;
3123 pThis->u.s.pvIp6Stream = NULL;
3124 pThis->u.s.pvArpStream = NULL;
3125 pThis->u.s.pvPromiscStream = NULL;
3126 pThis->u.s.hFastMtx = NIL_RTSEMFASTMUTEX;
3127 bzero(&pThis->u.s.Mac, sizeof(pThis->u.s.Mac));
3128 return VINF_SUCCESS;
3129}
3130
3131
3132bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
3133{
3134 /*
3135 * We don't support interface rediscovery on Solaris hosts because the
3136 * filter is very tightly bound to the stream.
3137 */
3138 return false;
3139}
3140
3141
3142int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, PINTNETSG pSG, uint32_t fDst)
3143{
3144 LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit pThis=%p pSG=%p fDst=%d\n", pThis, pSG, fDst));
3145
3146 int rc = VINF_SUCCESS;
3147 if (fDst & INTNETTRUNKDIR_WIRE)
3148 {
3149#ifdef VBOXNETFLT_SOLARIS_USE_NETINFO
3150 /*
3151 * @todo try find a way for IPFilter to accept ethernet frames (currently silently drops them).
3152 */
3153 mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
3154 if (RT_LIKELY(pMsg))
3155 {
3156 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
3157 unsigned uProtocol;
3158 if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
3159 uProtocol = AF_INET6;
3160 else if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
3161 uProtocol = AF_INET;
3162
3163 /*
3164 * Queue out using netinfo.
3165 */
3166 netstack_t *pNetStack = netstack_get_current();
3167 if (pNetStack)
3168 {
3169 net_data_t pNetData = net_lookup_impl(NHF_INET, pNetStack);
3170 if (pNetData)
3171 {
3172 phy_if_t pInterface = net_phylookup(pNetData, pThis->szName);
3173 if (pInterface)
3174 {
3175 net_inject_t InjectData;
3176 InjectData.ni_packet = pMsg;
3177 InjectData.ni_physical = pInterface;
3178 bzero(&InjectData.ni_addr, sizeof(InjectData.ni_addr));
3179 InjectData.ni_addr.ss_family = uProtocol;
3180
3181 /*
3182 * Queue out rather than direct out transmission.
3183 */
3184 int rc = net_inject(pNetData, NI_QUEUE_OUT, &InjectData);
3185 if (!rc)
3186 rc = VINF_SUCCESS;
3187 else
3188 {
3189 LogRel((DEVICE_NAME ":queuing IP packet for transmission failed. rc=%d\n", rc));
3190 rc = VERR_NET_IO_ERROR;
3191 }
3192 }
3193 else
3194 {
3195 LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to lookup physical interface.\n"));
3196 rc = VERR_NET_IO_ERROR;
3197 }
3198 }
3199 else
3200 {
3201 LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to get IP hooks.\n"));
3202 rc = VERR_NET_IO_ERROR;
3203 }
3204 netstack_rele(pNetStack);
3205 }
3206 else
3207 {
3208 LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit failed to get current net stack.\n"));
3209 rc = VERR_NET_IO_ERROR;
3210 }
3211 }
3212 else
3213 {
3214 LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
3215 rc = VERR_NO_MEMORY;
3216 }
3217#else
3218 vboxnetflt_promisc_stream_t *pPromiscStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvPromiscStream);
3219 if (RT_LIKELY(pPromiscStream))
3220 {
3221 mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
3222 if (RT_LIKELY(pMsg))
3223 {
3224 LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_WIRE\n"));
3225
3226 vboxNetFltSolarisQueueLoopback(pThis, pPromiscStream, pMsg);
3227 putnext(WR(pPromiscStream->Stream.pReadQueue), pMsg);
3228 }
3229 else
3230 {
3231 LogRel((DEVICE_NAME ":vboxNetFltPortOsXmit vboxNetFltSolarisMBlkFromSG failed.\n"));
3232 rc = VERR_NO_MEMORY;
3233 }
3234 }
3235#endif
3236 }
3237
3238 if (fDst & INTNETTRUNKDIR_HOST)
3239 {
3240 /*
3241 * For unplumbed interfaces we would not be bound to IP or ARP.
3242 * We either bind to both or neither; so atomic reading one should be sufficient.
3243 */
3244 vboxnetflt_stream_t *pIp4Stream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvIp4Stream);
3245 if (!pIp4Stream)
3246 return rc;
3247
3248 /*
3249 * Create a message block and send it up the host stack (upstream).
3250 */
3251 mblk_t *pMsg = vboxNetFltSolarisMBlkFromSG(pThis, pSG, fDst);
3252 if (RT_LIKELY(pMsg))
3253 {
3254 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pMsg->b_rptr;
3255
3256 /*
3257 * Send message up ARP stream.
3258 */
3259 if (pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
3260 {
3261 LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST ARP\n"));
3262
3263 vboxnetflt_stream_t *pArpStream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvArpStream);
3264 if (pArpStream)
3265 {
3266 /*
3267 * Construct a DL_UNITDATA_IND style message for ARP as it doesn't understand fast path.
3268 */
3269 mblk_t *pDlpiMsg;
3270 int rc = vboxNetFltSolarisRawToUnitData(pMsg, &pDlpiMsg);
3271 if (RT_SUCCESS(rc))
3272 {
3273 pMsg = pDlpiMsg;
3274
3275 queue_t *pArpReadQueue = pArpStream->pReadQueue;
3276 putnext(pArpReadQueue, pMsg);
3277 }
3278 else
3279 {
3280 LogRel((DEVICE_NAME ":vboxNetFltSolarisRawToUnitData failed!\n"));
3281 freemsg(pMsg);
3282 rc = VERR_NO_MEMORY;
3283 }
3284 }
3285 else
3286 freemsg(pMsg); /* Should really never happen... */
3287 }
3288 else
3289 {
3290 vboxnetflt_stream_t *pIp6Stream = ASMAtomicUoReadPtr((void * volatile *)&pThis->u.s.pvIp6Stream);
3291 if ( pEthHdr->EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6)
3292 && pIp6Stream)
3293 {
3294 /*
3295 * Send messages up IPv6 stream.
3296 */
3297 LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv6\n"));
3298
3299 pMsg->b_rptr += sizeof(RTNETETHERHDR);
3300 queue_t *pIp6ReadQueue = pIp6Stream->pReadQueue;
3301 putnext(pIp6ReadQueue, pMsg);
3302 }
3303 else
3304 {
3305 /*
3306 * Send messages up IPv4 stream.
3307 */
3308 LogFlow((DEVICE_NAME ":vboxNetFltPortOsXmit INTNETTRUNKDIR_HOST IPv4\n"));
3309
3310 pMsg->b_rptr += sizeof(RTNETETHERHDR);
3311 queue_t *pIp4ReadQueue = pIp4Stream->pReadQueue;
3312 putnext(pIp4ReadQueue, pMsg);
3313 }
3314 }
3315 }
3316 else
3317 {
3318 LogRel((DEVICE_NAME ":vboxNetFltSolarisMBlkFromSG failed.\n"));
3319 rc = VERR_NO_MEMORY;
3320 }
3321 }
3322
3323 return rc;
3324}
3325
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette