VirtualBox

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

Last change on this file since 55998 was 52618, checked in by vboxsync, 10 years ago

HostDrivers, Runtime, Devices, Additions: TSC delta measurement and other changes resulting from bumping supdrv major version. TSC delta measurement currently disabled.

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