VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetFlt/VBoxNetFlt.c@ 91523

Last change on this file since 91523 was 85126, checked in by vboxsync, 4 years ago

iprt/cdefs.h,*: Adding DECL_HIDDEN_CALLBACK to shorten the relatively common DECLHIDDEN(DECLCALLBACK()) combination and to help tweaking DECLHIDDEN to include nothrow. bugref:9794

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 56.1 KB
Line 
1/* $Id: VBoxNetFlt.c 85126 2020-07-08 23:04:57Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Common Code.
4 */
5
6/*
7 * Copyright (C) 2008-2020 Oracle Corporation
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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/** @page pg_netflt VBoxNetFlt - Network Interface Filter
28 *
29 * This is a kernel module that attaches to a real interface on the host and
30 * filters and injects packets.
31 *
32 * In the big picture we're one of the three trunk interface on the internal
33 * network, the one named "NIC Filter Driver": @image html Networking_Overview.gif
34 *
35 *
36 * @section sec_netflt_locking Locking and Potential Races
37 *
38 * The main challenge here is to make sure the netfilter and internal network
39 * instances won't be destroyed while someone is calling into them.
40 *
41 * The main calls into or out of of the filter driver are:
42 * - Send.
43 * - Async send completion (not implemented yet)
44 * - Release by the internal network.
45 * - Receive.
46 * - Disappearance of the host networking interface.
47 * - Reappearance of the host networking interface.
48 *
49 * The latter two calls are can be caused by driver unloading/loading or the
50 * device being physical unplugged (e.g. a USB network device). Actually, the
51 * unload scenario must fervently be prevent as it will cause panics because the
52 * internal network will assume the trunk is around until it releases it.
53 * @todo Need to figure which host allow unloading and block/fix it.
54 *
55 * Currently the netfilter instance lives until the internal network releases
56 * it. So, it is the internal networks responsibility to make sure there are no
57 * active calls when it releases the trunk and destroys the network. The
58 * netfilter assists in this by providing INTNETTRUNKIFPORT::pfnSetState and
59 * INTNETTRUNKIFPORT::pfnWaitForIdle. The trunk state is used to enable/disable
60 * promiscuous mode on the hardware NIC (or similar activation) as well
61 * indicating that disconnect is imminent and no further calls shall be made
62 * into the internal network. After changing the state to disconnecting and
63 * prior to invoking INTNETTRUNKIFPORT::pfnDisconnectAndRelease, the internal
64 * network will use INTNETTRUNKIFPORT::pfnWaitForIdle to wait for any still
65 * active calls to complete.
66 *
67 * The netfilter employs a busy counter and an internal state in addition to the
68 * public trunk state. All these variables are protected using a spinlock.
69 *
70 *
71 * @section sec_netflt_msc Locking / Sequence Diagrams - OBSOLETE
72 *
73 * !OBSOLETE! - THIS WAS THE OLD APPROACH!
74 *
75 * This secion contains a few sequence diagrams describing the problematic
76 * transitions of a host interface filter instance.
77 *
78 * The thing that makes it all a bit problematic is that multiple events may
79 * happen at the same time, and that we have to be very careful to avoid
80 * deadlocks caused by mixing our locks with the ones in the host kernel. The
81 * main events are receive, send, async send completion, disappearance of the
82 * host networking interface and its reappearance. The latter two events are
83 * can be caused by driver unloading/loading or the device being physical
84 * unplugged (e.g. a USB network device).
85 *
86 * The strategy for dealing with these issues are:
87 * - Use a simple state machine.
88 * - Require the user (IntNet) to serialize all its calls to us,
89 * while at the same time not owning any lock used by any of the
90 * the callbacks we might call on receive and async send completion.
91 * - Make sure we're 100% idle before disconnecting, and have a
92 * disconnected status on both sides to fend off async calls.
93 * - Protect the host specific interface handle and the state variables
94 * using a spinlock.
95 *
96 *
97 * @subsection subsec_netflt_msc_dis_rel Disconnect from the network and release - OBSOLETE
98 *
99 * @msc
100 * VM, IntNet, NetFlt, Kernel, Wire;
101 *
102 * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
103 * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
104 * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
105 * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
106 * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
107 * NetFlt=>Kernel [label="pkt0 to wire", linecolor="green", textcolor="green"];
108 * Kernel->Wire [label="pkt0 to wire", linecolor="green", textcolor="green"];
109 *
110 * --- [label="Suspending the trunk interface"];
111 * IntNet=>IntNet [label="Lock Network"];
112 *
113 * Wire->Kernel [label="pkt1 - racing us", linecolor="red", textcolor="red"];
114 * Kernel=>>NetFlt [label="pkt1 - racing us", linecolor="red", textcolor="red"];
115 * NetFlt=>>IntNet [label="pkt1 recv - blocks", linecolor="red", textcolor="red"];
116 *
117 * IntNet=>IntNet [label="Mark Trunk Suspended"];
118 * IntNet=>IntNet [label="Unlock Network"];
119 *
120 * IntNet=>NetFlt [label="pfnSetActive(false)"];
121 * NetFlt=>NetFlt [label="Mark inactive (atomic)"];
122 * IntNet<<NetFlt;
123 * IntNet=>NetFlt [label="pfnWaitForIdle(forever)"];
124 *
125 * IntNet=>>NetFlt [label="pkt1 to host", linecolor="red", textcolor="red"];
126 * NetFlt=>>Kernel [label="pkt1 to host", linecolor="red", textcolor="red"];
127 *
128 * Kernel<-Wire [label="pkt0 on wire", linecolor="green", textcolor="green"];
129 * NetFlt<<Kernel [label="pkt0 on wire", linecolor="green", textcolor="green"];
130 * IntNet<<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
131 * IntNet<<=IntNet [label="Lock Net, free SG, Unlock Net", linecolor="green", textcolor="green"];
132 * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
133 * NetFlt<-NetFlt [label="idle", linecolor="green", textcolor="green"];
134 *
135 * IntNet<<NetFlt [label="idle (pfnWaitForIdle)"];
136 *
137 * Wire->Kernel [label="pkt2", linecolor="red", textcolor="red"];
138 * Kernel=>>NetFlt [label="pkt2", linecolor="red", textcolor="red"];
139 * NetFlt=>>Kernel [label="pkt2 to host", linecolor="red", textcolor="red"];
140 *
141 * VM->IntNet [label="pkt3", linecolor="green", textcolor="green"];
142 * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
143 * IntNet=>IntNet [label="Route packet -> drop", linecolor="green", textcolor="green" ];
144 * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
145 *
146 * --- [label="The trunk interface is idle now, disconnect it"];
147 * IntNet=>IntNet [label="Lock Network"];
148 * IntNet=>IntNet [label="Unlink Trunk"];
149 * IntNet=>IntNet [label="Unlock Network"];
150 * IntNet=>NetFlt [label="pfnDisconnectAndRelease"];
151 * NetFlt=>Kernel [label="iflt_detach"];
152 * NetFlt<<=Kernel [label="iff_detached"];
153 * NetFlt>>Kernel [label="iff_detached"];
154 * NetFlt<<Kernel [label="iflt_detach"];
155 * NetFlt=>NetFlt [label="Release"];
156 * IntNet<<NetFlt [label="pfnDisconnectAndRelease"];
157 *
158 * @endmsc
159 *
160 *
161 *
162 * @subsection subsec_netflt_msc_hif_rm Host Interface Removal - OBSOLETE
163 *
164 * The ifnet_t (pIf) is a tricky customer as any reference to it can potentially
165 * race the filter detaching. The simple way of solving it on Darwin is to guard
166 * all access to the pIf member with a spinlock. The other host systems will
167 * probably have similar race conditions, so the spinlock is a generic thing.
168 *
169 * @msc
170 * VM, IntNet, NetFlt, Kernel;
171 *
172 * VM->IntNet [label="pkt0", linecolor="green", textcolor="green"];
173 * IntNet=>IntNet [label="Lock Network", linecolor="green", textcolor="green" ];
174 * IntNet=>IntNet [label="Route packet -> wire", linecolor="green", textcolor="green" ];
175 * IntNet=>IntNet [label="Unlock Network", linecolor="green", textcolor="green" ];
176 * IntNet=>NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green" ];
177 * NetFlt=>Kernel [label="ifnet_reference w/ spinlock", linecolor="green", textcolor="green" ];
178 * NetFlt<<Kernel [label="ifnet_reference", linecolor="green", textcolor="green" ];
179 * NetFlt=>Kernel [label="pkt0 to wire (blocks)", linecolor="green", textcolor="green" ];
180 *
181 * --- [label="The host interface is being disconnected"];
182 * Kernel->NetFlt [label="iff_detached"];
183 * NetFlt=>Kernel [label="ifnet_release w/ spinlock"];
184 * NetFlt<<Kernel [label="ifnet_release"];
185 * NetFlt=>NetFlt [label="fDisconnectedFromHost=true"];
186 * NetFlt>>Kernel [label="iff_detached"];
187 *
188 * NetFlt<<Kernel [label="dropped", linecolor="green", textcolor="green"];
189 * NetFlt=>NetFlt [label="Acquire spinlock", linecolor="green", textcolor="green"];
190 * NetFlt=>Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
191 * NetFlt<<Kernel [label="ifnet_release", linecolor="green", textcolor="green"];
192 * NetFlt=>NetFlt [label="pIf=NULL", linecolor="green", textcolor="green"];
193 * NetFlt=>NetFlt [label="Release spinlock", linecolor="green", textcolor="green"];
194 * IntNet<=NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
195 * IntNet>>NetFlt [label="pfnSGRelease", linecolor="green", textcolor="green"];
196 * IntNet<<NetFlt [label="pkt0 to wire", linecolor="green", textcolor="green"];
197 *
198 * @endmsc
199 *
200 *
201 *
202 * @subsection subsec_netflt_msc_hif_rd Host Interface Rediscovery - OBSOLETE
203 *
204 * The rediscovery is performed when we receive a send request and a certain
205 * period have elapsed since the last attempt, i.e. we're polling it. We
206 * synchronize the rediscovery with disconnection from the internal network
207 * by means of the pfnWaitForIdle call, so no special handling is required.
208 *
209 * @msc
210 * VM2, VM1, IntNet, NetFlt, Kernel, Wire;
211 *
212 * --- [label="Rediscovery conditions are not met"];
213 * VM1->IntNet [label="pkt0"];
214 * IntNet=>IntNet [label="Lock Network"];
215 * IntNet=>IntNet [label="Route packet -> wire"];
216 * IntNet=>IntNet [label="Unlock Network"];
217 * IntNet=>NetFlt [label="pkt0 to wire"];
218 * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
219 * IntNet<<NetFlt [label="pkt0 to wire (dropped)"];
220 *
221 * --- [label="Rediscovery conditions"];
222 * VM1->IntNet [label="pkt1"];
223 * IntNet=>IntNet [label="Lock Network"];
224 * IntNet=>IntNet [label="Route packet -> wire"];
225 * IntNet=>IntNet [label="Unlock Network"];
226 * IntNet=>NetFlt [label="pkt1 to wire"];
227 * NetFlt=>NetFlt [label="Read pIf(==NULL) w/ spinlock"];
228 * NetFlt=>NetFlt [label="fRediscoveryPending=true w/ spinlock"];
229 * NetFlt=>Kernel [label="ifnet_find_by_name"];
230 * NetFlt<<Kernel [label="ifnet_find_by_name (success)"];
231 *
232 * VM2->IntNet [label="pkt2", linecolor="red", textcolor="red"];
233 * IntNet=>IntNet [label="Lock Network", linecolor="red", textcolor="red"];
234 * IntNet=>IntNet [label="Route packet -> wire", linecolor="red", textcolor="red"];
235 * IntNet=>IntNet [label="Unlock Network", linecolor="red", textcolor="red"];
236 * IntNet=>NetFlt [label="pkt2 to wire", linecolor="red", textcolor="red"];
237 * NetFlt=>NetFlt [label="!pIf || fRediscoveryPending (w/ spinlock)", linecolor="red", textcolor="red"];
238 * IntNet<<NetFlt [label="pkt2 to wire (dropped)", linecolor="red", textcolor="red"];
239
240 * NetFlt=>Kernel [label="iflt_attach"];
241 * NetFlt<<Kernel [label="iflt_attach (success)"];
242 * NetFlt=>NetFlt [label="Acquire spinlock"];
243 * NetFlt=>NetFlt [label="Set pIf and update flags"];
244 * NetFlt=>NetFlt [label="Release spinlock"];
245 *
246 * NetFlt=>Kernel [label="pkt1 to wire"];
247 * Kernel->Wire [label="pkt1 to wire"];
248 * NetFlt<<Kernel [label="pkt1 to wire"];
249 * IntNet<<NetFlt [label="pkt1 to wire"];
250 *
251 *
252 * @endmsc
253 *
254 */
255
256
257/*********************************************************************************************************************************
258* Header Files *
259*********************************************************************************************************************************/
260#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
261#include "VBoxNetFltInternal.h"
262
263#include <VBox/sup.h>
264#include <VBox/log.h>
265#include <VBox/err.h>
266#include <iprt/assert.h>
267#include <iprt/string.h>
268#include <iprt/spinlock.h>
269#include <iprt/uuid.h>
270#include <iprt/mem.h>
271#include <iprt/time.h>
272#include <iprt/semaphore.h>
273#include <iprt/thread.h>
274
275
276/*********************************************************************************************************************************
277* Defined Constants And Macros *
278*********************************************************************************************************************************/
279#define IFPORT_2_VBOXNETFLTINS(pIfPort) \
280 ( (PVBOXNETFLTINS)((uint8_t *)(pIfPort) - RT_UOFFSETOF(VBOXNETFLTINS, MyPort)) )
281
282
283AssertCompileMemberSize(VBOXNETFLTINS, enmState, sizeof(uint32_t));
284
285/**
286 * Sets the enmState member atomically.
287 *
288 * Used for all updates.
289 *
290 * @param pThis The instance.
291 * @param enmNewState The new value.
292 */
293DECLINLINE(void) vboxNetFltSetState(PVBOXNETFLTINS pThis, VBOXNETFTLINSSTATE enmNewState)
294{
295 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, enmNewState);
296}
297
298
299/**
300 * Gets the enmState member atomically.
301 *
302 * Used for all reads.
303 *
304 * @returns The enmState value.
305 * @param pThis The instance.
306 */
307DECLINLINE(VBOXNETFTLINSSTATE) vboxNetFltGetState(PVBOXNETFLTINS pThis)
308{
309 return (VBOXNETFTLINSSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState);
310}
311
312
313/**
314 * Finds a instance by its name, the caller does the locking.
315 *
316 * @returns Pointer to the instance by the given name. NULL if not found.
317 * @param pGlobals The globals.
318 * @param pszName The name of the instance.
319 */
320static PVBOXNETFLTINS vboxNetFltFindInstanceLocked(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
321{
322 PVBOXNETFLTINS pCur;
323 for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
324 if (!strcmp(pszName, pCur->szName))
325 return pCur;
326 return NULL;
327}
328
329
330/**
331 * Finds a instance by its name, will request the mutex.
332 *
333 * No reference to the instance is retained, we're assuming the caller to
334 * already have one but just for some reason doesn't have the pointer to it.
335 *
336 * @returns Pointer to the instance by the given name. NULL if not found.
337 * @param pGlobals The globals.
338 * @param pszName The name of the instance.
339 */
340DECLHIDDEN(PVBOXNETFLTINS) vboxNetFltFindInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName)
341{
342 PVBOXNETFLTINS pRet;
343 int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
344 AssertRCReturn(rc, NULL);
345
346 pRet = vboxNetFltFindInstanceLocked(pGlobals, pszName);
347
348 rc = RTSemFastMutexRelease(pGlobals->hFastMtx);
349 AssertRC(rc);
350 return pRet;
351}
352
353
354/**
355 * Unlinks an instance from the chain.
356 *
357 * @param pGlobals The globals.
358 * @param pToUnlink The instance to unlink.
359 */
360static void vboxNetFltUnlinkLocked(PVBOXNETFLTGLOBALS pGlobals, PVBOXNETFLTINS pToUnlink)
361{
362 if (pGlobals->pInstanceHead == pToUnlink)
363 pGlobals->pInstanceHead = pToUnlink->pNext;
364 else
365 {
366 PVBOXNETFLTINS pCur;
367 for (pCur = pGlobals->pInstanceHead; pCur; pCur = pCur->pNext)
368 if (pCur->pNext == pToUnlink)
369 {
370 pCur->pNext = pToUnlink->pNext;
371 break;
372 }
373 Assert(pCur);
374 }
375 pToUnlink->pNext = NULL;
376}
377
378
379/**
380 * Performs interface rediscovery if it was disconnected from the host.
381 *
382 * @returns true if successfully rediscovered and connected, false if not.
383 * @param pThis The instance.
384 */
385static bool vboxNetFltMaybeRediscovered(PVBOXNETFLTINS pThis)
386{
387 uint64_t Now;
388 bool fRediscovered;
389 bool fDoIt;
390
391 /*
392 * Don't do rediscovery if we're called with preemption disabled.
393 *
394 * Note! This may cause trouble if we're always called with preemption
395 * disabled and vboxNetFltOsMaybeRediscovered actually does some real
396 * work. For the time being though, only Darwin and FreeBSD depends
397 * on these call outs and neither supports sending with preemption
398 * disabled.
399 */
400 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
401 return false;
402
403 /*
404 * Rediscovered already? Time to try again?
405 */
406 Now = RTTimeNanoTS();
407 RTSpinlockAcquire(pThis->hSpinlock);
408
409 fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
410 fDoIt = !fRediscovered
411 && !ASMAtomicUoReadBool(&pThis->fRediscoveryPending)
412 && Now - ASMAtomicUoReadU64(&pThis->NanoTSLastRediscovery) > UINT64_C(5000000000); /* 5 sec */
413 if (fDoIt)
414 ASMAtomicWriteBool(&pThis->fRediscoveryPending, true);
415
416 RTSpinlockRelease(pThis->hSpinlock);
417
418 /*
419 * Call the OS specific code to do the job.
420 * Update the state when the call returns, that is everything except for
421 * the fDisconnectedFromHost flag which the OS specific code shall set.
422 */
423 if (fDoIt)
424 {
425 fRediscovered = vboxNetFltOsMaybeRediscovered(pThis);
426
427 Assert(!fRediscovered || !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost));
428
429 ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, RTTimeNanoTS());
430 ASMAtomicWriteBool(&pThis->fRediscoveryPending, false);
431
432 if (fRediscovered)
433 /** @todo this isn't 100% serialized. */
434 vboxNetFltPortOsSetActive(pThis, pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
435 }
436
437 return fRediscovered;
438}
439
440
441/**
442 * @copydoc INTNETTRUNKIFPORT::pfnXmit
443 */
444static DECLCALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
445{
446 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
447 int rc = VINF_SUCCESS;
448
449 /*
450 * Input validation.
451 */
452 AssertPtr(pThis);
453 AssertPtr(pSG);
454 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
455 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
456
457 /*
458 * Do a busy retain and then make sure we're connected to the interface
459 * before invoking the OS specific code.
460 */
461 if (RT_LIKELY(vboxNetFltTryRetainBusyActive(pThis)))
462 {
463 if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)
464 || vboxNetFltMaybeRediscovered(pThis))
465 rc = vboxNetFltPortOsXmit(pThis, pvIfData, pSG, fDst);
466 vboxNetFltRelease(pThis, true /* fBusy */);
467 }
468
469 return rc;
470}
471
472
473/**
474 * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
475 */
476static DECLCALLBACK(int) vboxNetFltPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
477{
478 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
479 int rc;
480
481 /*
482 * Input validation.
483 */
484 AssertPtr(pThis);
485 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
486 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
487 AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE);
488
489 /*
490 * Go to sleep on the semaphore after checking the busy count.
491 */
492 vboxNetFltRetain(pThis, false /* fBusy */);
493
494 rc = VINF_SUCCESS;
495 while (pThis->cBusy && RT_SUCCESS(rc))
496 rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */
497
498 vboxNetFltRelease(pThis, false /* fBusy */);
499
500 return rc;
501}
502
503
504/**
505 * @copydoc INTNETTRUNKIFPORT::pfnSetState
506 */
507static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
508{
509 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
510 INTNETTRUNKIFSTATE enmOldTrunkState;
511
512 /*
513 * Input validation.
514 */
515 AssertPtr(pThis);
516 AssertPtr(pThis->pGlobals);
517 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
518 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID);
519 AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END,
520 INTNETTRUNKIFSTATE_INVALID);
521
522 /*
523 * Take the lock and change the state.
524 */
525 RTSpinlockAcquire(pThis->hSpinlock);
526 enmOldTrunkState = pThis->enmTrunkState;
527 if (enmOldTrunkState != enmState)
528 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState);
529 RTSpinlockRelease(pThis->hSpinlock);
530
531 /*
532 * If the state change indicates that the trunk has become active or
533 * inactive, call the OS specific part so they can work the promiscuous
534 * settings and such.
535 * Note! The caller makes sure there are no concurrent pfnSetState calls.
536 */
537 if ((enmOldTrunkState == INTNETTRUNKIFSTATE_ACTIVE) != (enmState == INTNETTRUNKIFSTATE_ACTIVE))
538 vboxNetFltPortOsSetActive(pThis, (enmState == INTNETTRUNKIFSTATE_ACTIVE));
539
540 return enmOldTrunkState;
541}
542
543
544/**
545 * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress
546 */
547static DECLCALLBACK(void) vboxNetFltPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, void *pvIfData, PCRTMAC pMac)
548{
549 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
550
551 /*
552 * Input validation.
553 */
554 AssertPtr(pThis);
555 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
556
557 vboxNetFltRetain(pThis, false /* fBusy */);
558 vboxNetFltPortOsNotifyMacAddress(pThis, pvIfData, pMac);
559 vboxNetFltRelease(pThis, false /* fBusy */);
560}
561
562
563/**
564 * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface
565 */
566static DECLCALLBACK(int) vboxNetFltPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIf, void **ppvIfData)
567{
568 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
569 int rc;
570
571 /*
572 * Input validation.
573 */
574 AssertPtr(pThis);
575 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
576
577 vboxNetFltRetain(pThis, false /* fBusy */);
578 rc = vboxNetFltPortOsConnectInterface(pThis, pvIf, ppvIfData);
579 vboxNetFltRelease(pThis, false /* fBusy */);
580
581 return rc;
582}
583
584
585/**
586 * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface
587 */
588static DECLCALLBACK(void) vboxNetFltPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, void *pvIfData)
589{
590 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
591 int rc;
592
593 /*
594 * Input validation.
595 */
596 AssertPtr(pThis);
597 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
598
599 vboxNetFltRetain(pThis, false /* fBusy */);
600 rc = vboxNetFltPortOsDisconnectInterface(pThis, pvIfData);
601 vboxNetFltRelease(pThis, false /* fBusy */);
602 AssertRC(rc); /** @todo fix vboxNetFltPortOsDisconnectInterface. */
603}
604
605
606/**
607 * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
608 */
609static DECLCALLBACK(void) vboxNetFltPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
610{
611 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
612
613 /*
614 * Serious paranoia.
615 */
616 AssertPtr(pThis);
617 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
618 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
619 AssertPtr(pThis->pGlobals);
620 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
621 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
622 Assert(pThis->szName[0]);
623
624 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected);
625 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
626 Assert(!pThis->fRediscoveryPending);
627 Assert(!pThis->cBusy);
628
629 /*
630 * Disconnect and release it.
631 */
632 RTSpinlockAcquire(pThis->hSpinlock);
633 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
634 RTSpinlockRelease(pThis->hSpinlock);
635
636 vboxNetFltOsDisconnectIt(pThis);
637 pThis->pSwitchPort = NULL;
638
639#ifdef VBOXNETFLT_STATIC_CONFIG
640 RTSpinlockAcquire(pThis->hSpinlock);
641 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected);
642 RTSpinlockRelease(pThis->hSpinlock);
643#endif
644
645 vboxNetFltRelease(pThis, false /* fBusy */);
646}
647
648
649/**
650 * Destroy a device that has been disconnected from the switch.
651 *
652 * @returns true if the instance is destroyed, false otherwise.
653 * @param pThis The instance to be destroyed. This is
654 * no longer valid when this function returns.
655 */
656static bool vboxNetFltDestroyInstance(PVBOXNETFLTINS pThis)
657{
658 PVBOXNETFLTGLOBALS pGlobals = pThis->pGlobals;
659 uint32_t cRefs = ASMAtomicUoReadU32((uint32_t volatile *)&pThis->cRefs);
660 int rc;
661 LogFlow(("vboxNetFltDestroyInstance: pThis=%p (%s)\n", pThis, pThis->szName));
662
663 /*
664 * Validate the state.
665 */
666#ifdef VBOXNETFLT_STATIC_CONFIG
667 Assert( vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting
668 || vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
669#else
670 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting);
671#endif
672 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
673 Assert(!pThis->fRediscoveryPending);
674 Assert(!pThis->cRefs);
675 Assert(!pThis->cBusy);
676 Assert(!pThis->pSwitchPort);
677
678 /*
679 * Make sure the state is 'disconnecting' / 'destroying' and let the OS
680 * specific code do its part of the cleanup outside the mutex.
681 */
682 rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
683 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
684 RTSemFastMutexRelease(pGlobals->hFastMtx);
685
686 vboxNetFltOsDeleteInstance(pThis);
687
688 /*
689 * Unlink the instance and free up its resources.
690 */
691 rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
692 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroyed);
693 vboxNetFltUnlinkLocked(pGlobals, pThis);
694 RTSemFastMutexRelease(pGlobals->hFastMtx);
695
696 RTSemEventDestroy(pThis->hEventIdle);
697 pThis->hEventIdle = NIL_RTSEMEVENT;
698 RTSpinlockDestroy(pThis->hSpinlock);
699 pThis->hSpinlock = NIL_RTSPINLOCK;
700 RTMemFree(pThis);
701
702 NOREF(cRefs);
703
704 return true;
705}
706
707
708/**
709 * Releases a reference to the specified instance.
710 *
711 * This method will destroy the instance when the count reaches 0.
712 * It will also take care of decrementing the counter and idle wakeup.
713 *
714 * @param pThis The instance.
715 * @param fBusy Whether the busy counter should be decremented too.
716 */
717DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy)
718{
719 uint32_t cRefs;
720
721 /*
722 * Paranoid Android.
723 */
724 AssertPtr(pThis);
725 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
726 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
727 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
728 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
729 AssertPtr(pThis->pGlobals);
730 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
731 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
732 Assert(pThis->szName[0]);
733
734 /*
735 * Work the busy counter.
736 */
737 if (fBusy)
738 {
739 cRefs = ASMAtomicDecU32(&pThis->cBusy);
740 if (!cRefs)
741 {
742 int rc = RTSemEventSignal(pThis->hEventIdle);
743 AssertRC(rc);
744 }
745 else
746 Assert(cRefs < UINT32_MAX / 2);
747 }
748
749 /*
750 * The object reference counting.
751 */
752 cRefs = ASMAtomicDecU32(&pThis->cRefs);
753 if (!cRefs)
754 vboxNetFltDestroyInstance(pThis);
755 else
756 Assert(cRefs < UINT32_MAX / 2);
757}
758
759
760/**
761 * @copydoc INTNETTRUNKIFPORT::pfnRelease
762 */
763static DECLCALLBACK(void) vboxNetFltPortRelease(PINTNETTRUNKIFPORT pIfPort)
764{
765 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
766 vboxNetFltRelease(pThis, false /* fBusy */);
767}
768
769
770/**
771 * @callback_method_impl{FNINTNETTRUNKIFPORTRELEASEBUSY}
772 */
773DECL_HIDDEN_CALLBACK(void) vboxNetFltPortReleaseBusy(PINTNETTRUNKIFPORT pIfPort)
774{
775 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
776 vboxNetFltRelease(pThis, true /*fBusy*/);
777}
778
779
780/**
781 * Retains a reference to the specified instance and a busy reference too.
782 *
783 * @param pThis The instance.
784 * @param fBusy Whether the busy counter should be incremented as well.
785 */
786DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy)
787{
788 uint32_t cRefs;
789
790 /*
791 * Paranoid Android.
792 */
793 AssertPtr(pThis);
794 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
795 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
796 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
797 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
798 AssertPtr(pThis->pGlobals);
799 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
800 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
801 Assert(pThis->szName[0]);
802
803 /*
804 * Retain the object.
805 */
806 cRefs = ASMAtomicIncU32(&pThis->cRefs);
807 Assert(cRefs > 1 && cRefs < UINT32_MAX / 2);
808
809 /*
810 * Work the busy counter.
811 */
812 if (fBusy)
813 {
814 cRefs = ASMAtomicIncU32(&pThis->cBusy);
815 Assert(cRefs > 0 && cRefs < UINT32_MAX / 2);
816 }
817
818 NOREF(cRefs);
819}
820
821
822/**
823 * Tries to retain the device as busy if the trunk is active.
824 *
825 * This is used before calling pfnRecv or pfnPreRecv.
826 *
827 * @returns true if we succeeded in retaining a busy reference to the active
828 * device. false if we failed.
829 * @param pThis The instance.
830 */
831DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis)
832{
833 uint32_t cRefs;
834 bool fRc;
835
836 /*
837 * Paranoid Android.
838 */
839 AssertPtr(pThis);
840 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
841 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
842 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
843 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
844 AssertPtr(pThis->pGlobals);
845 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
846 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
847 Assert(pThis->szName[0]);
848
849 /*
850 * Do the retaining and checking behind the spinlock.
851 */
852 RTSpinlockAcquire(pThis->hSpinlock);
853 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
854 if (fRc)
855 {
856 cRefs = ASMAtomicIncU32(&pThis->cRefs);
857 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
858
859 cRefs = ASMAtomicIncU32(&pThis->cBusy);
860 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
861 }
862 RTSpinlockRelease(pThis->hSpinlock);
863
864 return fRc;
865}
866
867
868/**
869 * Tries to retain the device as busy if the trunk is not disconnecting.
870 *
871 * This is used before reporting stuff to the internal network.
872 *
873 * @returns true if we succeeded in retaining a busy reference to the active
874 * device. false if we failed.
875 * @param pThis The instance.
876 */
877DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis)
878{
879 uint32_t cRefs;
880 bool fRc;
881
882 /*
883 * Paranoid Android.
884 */
885 AssertPtr(pThis);
886 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
887 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
888 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
889 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
890 AssertPtr(pThis->pGlobals);
891 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
892 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
893 Assert(pThis->szName[0]);
894
895 /*
896 * Do the retaining and checking behind the spinlock.
897 */
898 RTSpinlockAcquire(pThis->hSpinlock);
899 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
900 || pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE;
901 if (fRc)
902 {
903 cRefs = ASMAtomicIncU32(&pThis->cRefs);
904 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
905
906 cRefs = ASMAtomicIncU32(&pThis->cBusy);
907 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
908 }
909 RTSpinlockRelease(pThis->hSpinlock);
910
911 return fRc;
912}
913
914
915/**
916 * @copydoc INTNETTRUNKIFPORT::pfnRetain
917 */
918static DECLCALLBACK(void) vboxNetFltPortRetain(PINTNETTRUNKIFPORT pIfPort)
919{
920 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
921 vboxNetFltRetain(pThis, false /* fBusy */);
922}
923
924
925/**
926 * Connects the instance to the specified switch port.
927 *
928 * Called while owning the lock. We're ASSUMING that the internal
929 * networking code is already owning an recursive mutex, so, there
930 * will be no deadlocks when vboxNetFltOsConnectIt calls back into
931 * it for setting preferences.
932 *
933 * @returns VBox status code.
934 * @param pThis The instance.
935 * @param pSwitchPort The port on the internal network 'switch'.
936 * @param ppIfPort Where to return our port interface.
937 */
938static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
939{
940 int rc;
941
942 /*
943 * Validate state.
944 */
945 Assert(!pThis->fRediscoveryPending);
946 Assert(!pThis->cBusy);
947#ifdef VBOXNETFLT_STATIC_CONFIG
948 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
949#else
950 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Initializing);
951#endif
952 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
953
954 /*
955 * Do the job.
956 * Note that we're calling the os stuff while owning the semaphore here.
957 */
958 pThis->pSwitchPort = pSwitchPort;
959 rc = vboxNetFltOsConnectIt(pThis);
960 if (RT_SUCCESS(rc))
961 {
962 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Connected);
963 *ppIfPort = &pThis->MyPort;
964 }
965 else
966 pThis->pSwitchPort = NULL;
967
968 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
969 return rc;
970}
971
972
973/**
974 * Creates a new instance.
975 *
976 * The new instance will be in the suspended state in a dynamic config and in
977 * the inactive in a static one.
978 *
979 * Called without owning the lock, but will request is several times.
980 *
981 * @returns VBox status code.
982 * @param pGlobals The globals.
983 * @param pszName The instance name.
984 * @param pSwitchPort The port on the switch that we're connected with (dynamic only).
985 * @param fNoPromisc Do not attempt going into promiscuous mode.
986 * @param pvContext Context argument for vboxNetFltOsInitInstance.
987 * @param ppIfPort Where to store the pointer to our port interface (dynamic only).
988 */
989static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort,
990 bool fNoPromisc, void *pvContext, PINTNETTRUNKIFPORT *ppIfPort)
991{
992 /*
993 * Allocate and initialize a new instance before requesting the mutex.
994 * Note! That in a static config we'll initialize the trunk state to
995 * disconnecting and flip it in vboxNetFltFactoryCreateAndConnect
996 * later on. This better reflext the state and it works better with
997 * assertions in the destruction path.
998 */
999 int rc;
1000 size_t const cchName = strlen(pszName);
1001 PVBOXNETFLTINS pNew = (PVBOXNETFLTINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1]));
1002 if (!pNew)
1003 return VERR_INTNET_FLT_IF_FAILED;
1004 AssertMsg(((uintptr_t)pNew & 7) == 0, ("%p LB %#x\n", pNew, RT_UOFFSETOF_DYN(VBOXNETFLTINS, szName[cchName + 1])));
1005 pNew->pNext = NULL;
1006 pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
1007 pNew->MyPort.pfnRetain = vboxNetFltPortRetain;
1008 pNew->MyPort.pfnRelease = vboxNetFltPortRelease;
1009 pNew->MyPort.pfnDisconnectAndRelease= vboxNetFltPortDisconnectAndRelease;
1010 pNew->MyPort.pfnSetState = vboxNetFltPortSetState;
1011 pNew->MyPort.pfnWaitForIdle = vboxNetFltPortWaitForIdle;
1012 pNew->MyPort.pfnXmit = vboxNetFltPortXmit;
1013 pNew->MyPort.pfnNotifyMacAddress = vboxNetFltPortNotifyMacAddress;
1014 pNew->MyPort.pfnConnectInterface = vboxNetFltPortConnectInterface;
1015 pNew->MyPort.pfnDisconnectInterface = vboxNetFltPortDisconnectInterface;
1016 pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
1017 pNew->pSwitchPort = pSwitchPort;
1018 pNew->pGlobals = pGlobals;
1019 pNew->hSpinlock = NIL_RTSPINLOCK;
1020 pNew->enmState = kVBoxNetFltInsState_Initializing;
1021#ifdef VBOXNETFLT_STATIC_CONFIG
1022 pNew->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
1023#else
1024 pNew->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE;
1025#endif
1026 pNew->fDisconnectedFromHost = false;
1027 pNew->fRediscoveryPending = false;
1028 pNew->fDisablePromiscuous = fNoPromisc;
1029 pNew->NanoTSLastRediscovery = INT64_MAX;
1030 pNew->cRefs = 1;
1031 pNew->cBusy = 0;
1032 pNew->hEventIdle = NIL_RTSEMEVENT;
1033 memcpy(pNew->szName, pszName, cchName + 1);
1034
1035 rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxNetFltNewInstance");
1036 if (RT_SUCCESS(rc))
1037 {
1038 rc = RTSemEventCreate(&pNew->hEventIdle);
1039 if (RT_SUCCESS(rc))
1040 {
1041 rc = vboxNetFltOsPreInitInstance(pNew);
1042 if (RT_SUCCESS(rc))
1043 {
1044 /*
1045 * Insert the instance into the chain, checking for
1046 * duplicates first of course (race).
1047 */
1048 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1049 if (RT_SUCCESS(rc))
1050 {
1051 if (!vboxNetFltFindInstanceLocked(pGlobals, pszName))
1052 {
1053 pNew->pNext = pGlobals->pInstanceHead;
1054 pGlobals->pInstanceHead = pNew;
1055 RTSemFastMutexRelease(pGlobals->hFastMtx);
1056
1057 /*
1058 * Call the OS specific initialization code.
1059 */
1060 rc = vboxNetFltOsInitInstance(pNew, pvContext);
1061 RTSemFastMutexRequest(pGlobals->hFastMtx);
1062 if (RT_SUCCESS(rc))
1063 {
1064#ifdef VBOXNETFLT_STATIC_CONFIG
1065 /*
1066 * Static instances are unconnected at birth.
1067 */
1068 Assert(!pSwitchPort);
1069 pNew->enmState = kVBoxNetFltInsState_Unconnected;
1070 RTSemFastMutexRelease(pGlobals->hFastMtx);
1071 *ppIfPort = &pNew->MyPort;
1072 return rc;
1073
1074#else /* !VBOXNETFLT_STATIC_CONFIG */
1075 /*
1076 * Connect it as well, the OS specific bits has to be done outside
1077 * the lock as they may call back to into intnet.
1078 */
1079 rc = vboxNetFltConnectIt(pNew, pSwitchPort, ppIfPort);
1080 if (RT_SUCCESS(rc))
1081 {
1082 RTSemFastMutexRelease(pGlobals->hFastMtx);
1083 Assert(*ppIfPort == &pNew->MyPort);
1084 return rc;
1085 }
1086
1087 /* Bail out (failed). */
1088 vboxNetFltOsDeleteInstance(pNew);
1089#endif /* !VBOXNETFLT_STATIC_CONFIG */
1090 }
1091 vboxNetFltUnlinkLocked(pGlobals, pNew);
1092 }
1093 else
1094 rc = VERR_INTNET_FLT_IF_BUSY;
1095 RTSemFastMutexRelease(pGlobals->hFastMtx);
1096 }
1097 }
1098 RTSemEventDestroy(pNew->hEventIdle);
1099 }
1100 RTSpinlockDestroy(pNew->hSpinlock);
1101 }
1102
1103 RTMemFree(pNew);
1104 return rc;
1105}
1106
1107
1108#ifdef VBOXNETFLT_STATIC_CONFIG
1109/**
1110 * Searches for the NetFlt instance by its name and creates the new one if not found.
1111 *
1112 * @returns VBox status code.
1113 * @retval VINF_SUCCESS and *ppInstance if a new instance was created.
1114 * @retval VINF_ALREADY_INITIALIZED and *ppInstance if an instance already exists.
1115 *
1116 * @param pGlobal Pointer to the globals.
1117 * @param pszName The instance name.
1118 * @param ppInstance Where to return the instance pointer on success.
1119 * @param pvContext Context which needs to be passed along to vboxNetFltOsInitInstance.
1120 */
1121DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void *pvContext)
1122{
1123 PINTNETTRUNKIFPORT pIfPort;
1124 PVBOXNETFLTINS pCur;
1125 VBOXNETFTLINSSTATE enmState;
1126 int rc;
1127
1128 *ppInstance = NULL;
1129 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1130 AssertRCReturn(rc, rc);
1131
1132 /*
1133 * Look for an existing instance in the list.
1134 *
1135 * There might be an existing one in the list if the driver was unbound
1136 * while it was connected to an internal network. We're running into
1137 * a destruction race that is a bit similar to the one in
1138 * vboxNetFltFactoryCreateAndConnect, only the roles are reversed
1139 * and we're not in a position to back down. Instead of backing down
1140 * we'll delay a bit giving the other thread time to complete the
1141 * destructor.
1142 */
1143 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1144 while (pCur)
1145 {
1146 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
1147 if (cRefs > 1)
1148 {
1149 enmState = vboxNetFltGetState(pCur);
1150 switch (enmState)
1151 {
1152 case kVBoxNetFltInsState_Unconnected:
1153 case kVBoxNetFltInsState_Connected:
1154 case kVBoxNetFltInsState_Disconnecting:
1155 if (pCur->fDisconnectedFromHost)
1156 {
1157 /* Wait for it to exit the transitional disconnecting
1158 state. It might otherwise be running the risk of
1159 upsetting the OS specific code... */
1160 /** @todo This reconnect stuff should be serialized correctly for static
1161 * devices. Shouldn't it? In the dynamic case we're using the INTNET
1162 * outbound thunk lock, but that doesn't quite cut it here, or does
1163 * it? We could either transition to initializing or make a callback
1164 * while owning the mutex here... */
1165 if (enmState == kVBoxNetFltInsState_Disconnecting)
1166 {
1167 do
1168 {
1169 RTSemFastMutexRelease(pGlobals->hFastMtx);
1170 RTThreadSleep(2); /* (2ms) */
1171 RTSemFastMutexRequest(pGlobals->hFastMtx);
1172 enmState = vboxNetFltGetState(pCur);
1173 }
1174 while (enmState == kVBoxNetFltInsState_Disconnecting);
1175 AssertMsg(enmState == kVBoxNetFltInsState_Unconnected, ("%d\n", enmState));
1176 Assert(pCur->fDisconnectedFromHost);
1177 }
1178
1179 RTSemFastMutexRelease(pGlobals->hFastMtx);
1180 *ppInstance = pCur;
1181 return VINF_ALREADY_INITIALIZED;
1182 }
1183 /* fall thru */
1184
1185 default:
1186 {
1187 bool fDfH = pCur->fDisconnectedFromHost;
1188 RTSemFastMutexRelease(pGlobals->hFastMtx);
1189 vboxNetFltRelease(pCur, false /* fBusy */);
1190 LogRel(("VBoxNetFlt: Huh? An instance of '%s' already exists! [pCur=%p cRefs=%d fDfH=%RTbool enmState=%d]\n",
1191 pszName, pCur, cRefs - 1, fDfH, enmState));
1192 *ppInstance = NULL;
1193 return VERR_INTNET_FLT_IF_BUSY;
1194 }
1195 }
1196 }
1197
1198 /* Zero references, it's being destroyed. Delay a bit so the destructor
1199 can finish its work and try again. (vboxNetFltNewInstance will fail
1200 with duplicate name if we don't.) */
1201# ifdef RT_STRICT
1202 Assert(cRefs == 1);
1203 enmState = vboxNetFltGetState(pCur);
1204 AssertMsg( enmState == kVBoxNetFltInsState_Unconnected
1205 || enmState == kVBoxNetFltInsState_Disconnecting
1206 || enmState == kVBoxNetFltInsState_Destroyed, ("%d\n", enmState));
1207# endif
1208 ASMAtomicDecU32(&pCur->cRefs);
1209 RTSemFastMutexRelease(pGlobals->hFastMtx);
1210 RTThreadSleep(2); /* (2ms) */
1211 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1212 AssertRCReturn(rc, rc);
1213
1214 /* try again */
1215 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1216 }
1217
1218 RTSemFastMutexRelease(pGlobals->hFastMtx);
1219
1220 /*
1221 * Try create a new instance.
1222 * (fNoPromisc is overridden in the vboxNetFltFactoryCreateAndConnect path, so pass true here.)
1223 */
1224 rc = vboxNetFltNewInstance(pGlobals, pszName, NULL, true /* fNoPromisc */, pvContext, &pIfPort);
1225 if (RT_SUCCESS(rc))
1226 *ppInstance = IFPORT_2_VBOXNETFLTINS(pIfPort);
1227 else
1228 *ppInstance = NULL;
1229
1230 return rc;
1231}
1232#endif /* VBOXNETFLT_STATIC_CONFIG */
1233
1234
1235/**
1236 * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
1237 */
1238static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
1239 PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
1240 PINTNETTRUNKIFPORT *ppIfPort)
1241{
1242 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
1243 PVBOXNETFLTINS pCur;
1244 int rc;
1245
1246 LogFlow(("vboxNetFltFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
1247 Assert(pGlobals->cFactoryRefs > 0);
1248 AssertMsgReturn(!(fFlags & ~(INTNETTRUNKFACTORY_FLAG_NO_PROMISC)),
1249 ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
1250
1251 /*
1252 * Static: Find instance, check if busy, connect if not.
1253 * Dynamic: Check for duplicate / busy interface instance.
1254 */
1255 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1256 AssertRCReturn(rc, rc);
1257
1258//#if defined(VBOXNETADP) && defined(RT_OS_WINDOWS)
1259// /* temporary hack to pick up the first adapter */
1260// pCur = pGlobals->pInstanceHead; /** @todo Don't for get to remove this temporary hack... :-) */
1261//#else
1262 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1263//#endif
1264 if (pCur)
1265 {
1266#ifdef VBOXNETFLT_STATIC_CONFIG
1267 /* Try grab a reference. If the count had already reached zero we're racing the
1268 destructor code and must back down. */
1269 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
1270 if (cRefs > 1)
1271 {
1272 if (vboxNetFltGetState(pCur) == kVBoxNetFltInsState_Unconnected)
1273 {
1274 pCur->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; /** @todo protect me? */
1275 pCur->fDisablePromiscuous = !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC);
1276 rc = vboxNetFltConnectIt(pCur, pSwitchPort, ppIfPort);
1277 if (RT_SUCCESS(rc))
1278 pCur = NULL; /* Don't release it, reference given to the caller. */
1279 else
1280 pCur->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
1281 }
1282 else
1283 rc = VERR_INTNET_FLT_IF_BUSY;
1284 }
1285 else
1286 {
1287 Assert(cRefs == 1);
1288 ASMAtomicDecU32(&pCur->cRefs);
1289 pCur = NULL; /* nothing to release */
1290 rc = VERR_INTNET_FLT_IF_NOT_FOUND;
1291 }
1292
1293 RTSemFastMutexRelease(pGlobals->hFastMtx);
1294 if (pCur)
1295 vboxNetFltRelease(pCur, false /* fBusy */);
1296#else
1297 rc = VERR_INTNET_FLT_IF_BUSY;
1298 RTSemFastMutexRelease(pGlobals->hFastMtx);
1299#endif
1300 LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
1301 return rc;
1302 }
1303
1304 RTSemFastMutexRelease(pGlobals->hFastMtx);
1305
1306#ifdef VBOXNETFLT_STATIC_CONFIG
1307 rc = VERR_INTNET_FLT_IF_NOT_FOUND;
1308#else
1309 /*
1310 * Dynamically create a new instance.
1311 */
1312 rc = vboxNetFltNewInstance(pGlobals,
1313 pszName,
1314 pSwitchPort,
1315 !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC),
1316 NULL,
1317 ppIfPort);
1318#endif
1319 LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
1320 return rc;
1321}
1322
1323
1324/**
1325 * @copydoc INTNETTRUNKFACTORY::pfnRelease
1326 */
1327static DECLCALLBACK(void) vboxNetFltFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
1328{
1329 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
1330
1331 int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
1332 Assert(cRefs >= 0); NOREF(cRefs);
1333 LogFlow(("vboxNetFltFactoryRelease: cRefs=%d (new)\n", cRefs));
1334}
1335
1336
1337/**
1338 * Implements the SUPDRV component factor interface query method.
1339 *
1340 * @returns Pointer to an interface. NULL if not supported.
1341 *
1342 * @param pSupDrvFactory Pointer to the component factory registration structure.
1343 * @param pSession The session - unused.
1344 * @param pszInterfaceUuid The factory interface id.
1345 */
1346static DECLCALLBACK(void *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession,
1347 const char *pszInterfaceUuid)
1348{
1349 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_UOFFSETOF(VBOXNETFLTGLOBALS, SupDrvFactory));
1350
1351 /*
1352 * Convert the UUID strings and compare them.
1353 */
1354 RTUUID UuidReq;
1355 int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
1356 if (RT_SUCCESS(rc))
1357 {
1358 if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR))
1359 {
1360 ASMAtomicIncS32(&pGlobals->cFactoryRefs);
1361 return &pGlobals->TrunkFactory;
1362 }
1363#ifdef LOG_ENABLED
1364 /* log legacy queries */
1365 /* else if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_V1_UUID_STR))
1366 Log(("VBoxNetFlt: V1 factory query\n"));
1367 */
1368 else
1369 Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid));
1370#endif
1371 }
1372 else
1373 Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
1374
1375 RT_NOREF1(pSession);
1376 return NULL;
1377}
1378
1379
1380/**
1381 * Checks whether the VBoxNetFlt wossname can be unloaded.
1382 *
1383 * This will return false if someone is currently using the module.
1384 *
1385 * @returns true if it's relatively safe to unload it, otherwise false.
1386 * @param pGlobals Pointer to the globals.
1387 */
1388DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals)
1389{
1390 int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1391 bool fRc = !pGlobals->pInstanceHead
1392 && pGlobals->cFactoryRefs <= 0;
1393 RTSemFastMutexRelease(pGlobals->hFastMtx);
1394 AssertRC(rc);
1395 return fRc;
1396}
1397
1398
1399/**
1400 * Try to close the IDC connection to SUPDRV if established.
1401 *
1402 * @returns VBox status code.
1403 * @retval VINF_SUCCESS on success.
1404 * @retval VERR_WRONG_ORDER if we're busy.
1405 *
1406 * @param pGlobals Pointer to the globals.
1407 *
1408 * @sa vboxNetFltTryDeleteIdcAndGlobals()
1409 */
1410DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals)
1411{
1412 int rc;
1413
1414 Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
1415
1416 /*
1417 * Check before trying to deregister the factory.
1418 */
1419 if (!vboxNetFltCanUnload(pGlobals))
1420 return VERR_WRONG_ORDER;
1421
1422 if (!pGlobals->fIDCOpen)
1423 rc = VINF_SUCCESS;
1424 else
1425 {
1426 /*
1427 * Disconnect from SUPDRV and check that nobody raced us,
1428 * reconnect if that should happen.
1429 */
1430 rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1431 AssertRC(rc);
1432 if (!vboxNetFltCanUnload(pGlobals))
1433 {
1434 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1435 AssertRC(rc);
1436 return VERR_WRONG_ORDER;
1437 }
1438
1439 SUPR0IdcClose(&pGlobals->SupDrvIDC);
1440 pGlobals->fIDCOpen = false;
1441 }
1442
1443 return rc;
1444}
1445
1446
1447/**
1448 * Establishes the IDC connection to SUPDRV and registers our component factory.
1449 *
1450 * @returns VBox status code.
1451 * @param pGlobals Pointer to the globals.
1452 * @sa vboxNetFltInitGlobalsAndIdc().
1453 */
1454DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals)
1455{
1456 int rc;
1457 Assert(!pGlobals->fIDCOpen);
1458
1459 /*
1460 * Establish a connection to SUPDRV and register our component factory.
1461 */
1462 rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
1463 if (RT_SUCCESS(rc))
1464 {
1465 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1466 if (RT_SUCCESS(rc))
1467 {
1468 pGlobals->fIDCOpen = true;
1469 Log(("VBoxNetFlt: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
1470 return rc;
1471 }
1472
1473 /* bail out. */
1474 LogRel(("VBoxNetFlt: Failed to register component factory, rc=%Rrc\n", rc));
1475 SUPR0IdcClose(&pGlobals->SupDrvIDC);
1476 }
1477
1478 return rc;
1479}
1480
1481
1482/**
1483 * Deletes the globals.
1484 *
1485 * This must be called after the IDC connection has been closed,
1486 * see vboxNetFltTryDeleteIdc().
1487 *
1488 * @param pGlobals Pointer to the globals.
1489 * @sa vboxNetFltTryDeleteIdcAndGlobals()
1490 */
1491DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals)
1492{
1493 Assert(!pGlobals->fIDCOpen);
1494
1495 /*
1496 * Release resources.
1497 */
1498 RTSemFastMutexDestroy(pGlobals->hFastMtx);
1499 pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
1500}
1501
1502
1503/**
1504 * Initializes the globals.
1505 *
1506 * @returns VBox status code.
1507 * @param pGlobals Pointer to the globals.
1508 * @sa vboxNetFltInitGlobalsAndIdc().
1509 */
1510DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals)
1511{
1512 /*
1513 * Initialize the common portions of the structure.
1514 */
1515 int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
1516 if (RT_SUCCESS(rc))
1517 {
1518 pGlobals->pInstanceHead = NULL;
1519
1520 pGlobals->TrunkFactory.pfnRelease = vboxNetFltFactoryRelease;
1521 pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetFltFactoryCreateAndConnect;
1522#if defined(RT_OS_WINDOWS) && defined(VBOXNETADP)
1523 memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp"));
1524#else
1525 memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetFlt", sizeof("VBoxNetFlt"));
1526#endif
1527 pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetFltQueryFactoryInterface;
1528 pGlobals->fIDCOpen = false;
1529
1530 return rc;
1531 }
1532
1533 return rc;
1534}
1535
1536
1537/**
1538 * Called by the native part when the OS wants the driver to unload.
1539 *
1540 * @returns VINF_SUCCESS on success, VERR_WRONG_ORDER if we're busy.
1541 *
1542 * @param pGlobals Pointer to the globals.
1543 */
1544DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals)
1545{
1546 int rc = vboxNetFltTryDeleteIdc(pGlobals);
1547 if (RT_SUCCESS(rc))
1548 vboxNetFltDeleteGlobals(pGlobals);
1549 return rc;
1550}
1551
1552
1553/**
1554 * Called by the native driver/kext module initialization routine.
1555 *
1556 * It will initialize the common parts of the globals, assuming the caller
1557 * has already taken care of the OS specific bits, and establish the IDC
1558 * connection to SUPDRV.
1559 *
1560 * @returns VBox status code.
1561 * @param pGlobals Pointer to the globals.
1562 */
1563DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals)
1564{
1565 /*
1566 * Initialize the common portions of the structure.
1567 */
1568 int rc = vboxNetFltInitGlobals(pGlobals);
1569 if (RT_SUCCESS(rc))
1570 {
1571 rc = vboxNetFltInitIdc(pGlobals);
1572 if (RT_SUCCESS(rc))
1573 return rc;
1574
1575 /* bail out. */
1576 vboxNetFltDeleteGlobals(pGlobals);
1577 }
1578
1579 return rc;
1580}
1581
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