VirtualBox

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

Last change on this file since 29491 was 29491, checked in by vboxsync, 15 years ago

IntNet: added MAC address notification and connect/disconnect interface callbacks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 55.2 KB
Line 
1/* $Id: VBoxNetFlt.c 29491 2010-05-14 17:46:22Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Common Code.
4 */
5
6/*
7 * Copyright (C) 2008-2009 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 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
378 uint64_t Now;
379 bool fRediscovered;
380 bool fDoIt;
381
382 /*
383 * Don't do rediscovery if we're called with preemption disabled.
384 *
385 * Note! This may cause trouble if we're always called with preemptioni
386 * disabled and vboxNetFltOsMaybeRediscovered actually does some real
387 * work. For the time being though, only Darwin and FreeBSD depends
388 * on these call outs and neither supports sending with preemption
389 * disabled.
390 */
391 if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
392 return false;
393
394 /*
395 * Rediscovered already? Time to try again?
396 */
397 Now = RTTimeNanoTS();
398 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
399
400 fRediscovered = !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
401 fDoIt = !fRediscovered
402 && !ASMAtomicUoReadBool(&pThis->fRediscoveryPending)
403 && Now - ASMAtomicUoReadU64(&pThis->NanoTSLastRediscovery) > UINT64_C(5000000000); /* 5 sec */
404 if (fDoIt)
405 ASMAtomicWriteBool(&pThis->fRediscoveryPending, true);
406
407 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
408
409 /*
410 * Call the OS specific code to do the job.
411 * Update the state when the call returns, that is everything except for
412 * the fDisconnectedFromHost flag which the OS specific code shall set.
413 */
414 if (fDoIt)
415 {
416 fRediscovered = vboxNetFltOsMaybeRediscovered(pThis);
417
418 Assert(!fRediscovered || !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost));
419
420 ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, RTTimeNanoTS());
421 ASMAtomicWriteBool(&pThis->fRediscoveryPending, false);
422
423 if (fRediscovered)
424 /** @todo this isn't 100% serialized. */
425 vboxNetFltPortOsSetActive(pThis, pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE);
426 }
427
428 return fRediscovered;
429}
430
431
432/**
433 * @copydoc INTNETTRUNKIFPORT::pfnXmit
434 */
435static DECLCALLBACK(int) vboxNetFltPortXmit(PINTNETTRUNKIFPORT pIfPort, PINTNETSG pSG, uint32_t fDst)
436{
437 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
438 int rc = VINF_SUCCESS;
439
440 /*
441 * Input validation.
442 */
443 AssertPtr(pThis);
444 AssertPtr(pSG);
445 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
446 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
447
448 /*
449 * Do a busy retain and then make sure we're connected to the interface
450 * before invoking the OS specific code.
451 */
452 if (RT_LIKELY(vboxNetFltTryRetainBusyActive(pThis)))
453 {
454 if ( !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost)
455 || vboxNetFltMaybeRediscovered(pThis))
456 rc = vboxNetFltPortOsXmit(pThis, pSG, fDst);
457 vboxNetFltRelease(pThis, true /* fBusy */);
458 }
459
460 return rc;
461}
462
463
464/**
465 * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
466 */
467static DECLCALLBACK(int) vboxNetFltPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
468{
469 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
470 int rc;
471
472 /*
473 * Input validation.
474 */
475 AssertPtr(pThis);
476 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
477 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, VERR_INVALID_STATE);
478 AssertReturn(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING, VERR_INVALID_STATE);
479
480 /*
481 * Go to sleep on the semaphore after checking the busy count.
482 */
483 vboxNetFltRetain(pThis, false /* fBusy */);
484
485 rc = VINF_SUCCESS;
486 while (pThis->cBusy && RT_SUCCESS(rc))
487 rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */
488
489 vboxNetFltRelease(pThis, false /* fBusy */);
490
491 return rc;
492}
493
494
495/**
496 * @copydoc INTNETTRUNKIFPORT::pfnSetState
497 */
498static DECLCALLBACK(INTNETTRUNKIFSTATE) vboxNetFltPortSetState(PINTNETTRUNKIFPORT pIfPort, INTNETTRUNKIFSTATE enmState)
499{
500 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
501 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
502 INTNETTRUNKIFSTATE enmOldTrunkState;
503
504 /*
505 * Input validation.
506 */
507 AssertPtr(pThis);
508 AssertPtr(pThis->pGlobals);
509 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
510 AssertReturn(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected, INTNETTRUNKIFSTATE_INVALID);
511 AssertReturn(enmState > INTNETTRUNKIFSTATE_INVALID && enmState < INTNETTRUNKIFSTATE_END,
512 INTNETTRUNKIFSTATE_INVALID);
513
514 /*
515 * Take the lock and change the state.
516 */
517 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
518 enmOldTrunkState = pThis->enmTrunkState;
519 if (enmOldTrunkState != enmState)
520 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmTrunkState, enmState);
521 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
522
523 /*
524 * If the state change indicates that the trunk has become active or
525 * inactive, call the OS specific part so they can work the promiscuous
526 * settings and such.
527 * Note! The caller makes sure there are no concurrent pfnSetState calls.
528 */
529 if ((enmOldTrunkState == INTNETTRUNKIFSTATE_ACTIVE) != (enmState == INTNETTRUNKIFSTATE_ACTIVE))
530 vboxNetFltPortOsSetActive(pThis, (enmState == INTNETTRUNKIFSTATE_ACTIVE));
531
532 return enmOldTrunkState;
533}
534
535
536/**
537 * @copydoc INTNETTRUNKIFPORT::pfnNotifyMacAddress
538 */
539static DECLCALLBACK(void) vboxNetFltPortNotifyMacAddress(PINTNETTRUNKIFPORT pIfPort, INTNETIFHANDLE hIf, PCRTMAC pMac)
540{
541 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
542
543 /*
544 * Input validation.
545 */
546 AssertPtr(pThis);
547 AssertPtr(pSG);
548 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
549
550 vboxNetFltRetain(pThis, false /* fBusy */);
551 vboxNetFltPortOsNotifyMacAddress(pThis, hIf, pMac);
552 vboxNetFltRelease(pThis, false /* fBusy */);
553}
554
555
556/**
557 * @copydoc INTNETTRUNKIFPORT::pfnConnectInterface
558 */
559static DECLCALLBACK(int) vboxNetFltPortConnectInterface(PINTNETTRUNKIFPORT pIfPort, INTNETIFHANDLE hIf)
560{
561 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
562 int rc = VINF_SUCCESS;
563
564 /*
565 * Input validation.
566 */
567 AssertPtr(pThis);
568 AssertPtr(pSG);
569 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
570
571 vboxNetFltRetain(pThis, false /* fBusy */);
572 rc = vboxNetFltPortOsConnectInterface(pThis, hIf);
573 vboxNetFltRelease(pThis, false /* fBusy */);
574
575 return rc;
576}
577
578
579/**
580 * @copydoc INTNETTRUNKIFPORT::pfnDisconnectInterface
581 */
582static DECLCALLBACK(int) vboxNetFltPortDisconnectInterface(PINTNETTRUNKIFPORT pIfPort, INTNETIFHANDLE hIf)
583{
584 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
585 int rc = VINF_SUCCESS;
586
587 /*
588 * Input validation.
589 */
590 AssertPtr(pThis);
591 AssertPtr(pSG);
592 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
593
594 vboxNetFltRetain(pThis, false /* fBusy */);
595 rc = vboxNetFltPortOsDisconnectInterface(pThis, hIf);
596 vboxNetFltRelease(pThis, false /* fBusy */);
597
598 return rc;
599}
600
601
602/**
603 * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
604 */
605static DECLCALLBACK(void) vboxNetFltPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
606{
607 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
608 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
609
610 /*
611 * Serious paranoia.
612 */
613 AssertPtr(pThis);
614 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
615 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
616 AssertPtr(pThis->pGlobals);
617 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
618 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
619 Assert(pThis->szName[0]);
620
621 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Connected);
622 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
623 Assert(!pThis->fRediscoveryPending);
624 Assert(!pThis->cBusy);
625
626 /*
627 * Disconnect and release it.
628 */
629 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
630 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
631 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
632
633 vboxNetFltOsDisconnectIt(pThis);
634 pThis->pSwitchPort = NULL;
635
636#ifdef VBOXNETFLT_STATIC_CONFIG
637 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
638 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Unconnected);
639 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
640#endif
641
642 vboxNetFltRelease(pThis, false /* fBusy */);
643}
644
645
646/**
647 * Destroy a device that has been disconnected from the switch.
648 *
649 * @returns true if the instance is destroyed, false otherwise.
650 * @param pThis The instance to be destroyed. This is
651 * no longer valid when this function returns.
652 */
653static bool vboxNetFltDestroyInstance(PVBOXNETFLTINS pThis)
654{
655 PVBOXNETFLTGLOBALS pGlobals = pThis->pGlobals;
656 uint32_t cRefs = ASMAtomicUoReadU32((uint32_t volatile *)&pThis->cRefs);
657 int rc;
658 LogFlow(("vboxNetFltDestroyInstance: pThis=%p (%s)\n", pThis, pThis->szName));
659
660 /*
661 * Validate the state.
662 */
663#ifdef VBOXNETFLT_STATIC_CONFIG
664 Assert( vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting
665 || vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
666#else
667 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Disconnecting);
668#endif
669 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_DISCONNECTING);
670 Assert(!pThis->fRediscoveryPending);
671 Assert(!pThis->cRefs);
672 Assert(!pThis->cBusy);
673 Assert(!pThis->pSwitchPort);
674
675 /*
676 * Make sure the state is 'disconnecting' / 'destroying' and let the OS
677 * specific code do its part of the cleanup outside the mutex.
678 */
679 rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
680 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Disconnecting);
681 RTSemFastMutexRelease(pGlobals->hFastMtx);
682
683 vboxNetFltOsDeleteInstance(pThis);
684
685 /*
686 * Unlink the instance and free up its resources.
687 */
688 rc = RTSemFastMutexRequest(pGlobals->hFastMtx); AssertRC(rc);
689 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Destroyed);
690 vboxNetFltUnlinkLocked(pGlobals, pThis);
691 RTSemFastMutexRelease(pGlobals->hFastMtx);
692
693 RTSemEventDestroy(pThis->hEventIdle);
694 pThis->hEventIdle = NIL_RTSEMEVENT;
695 RTSpinlockDestroy(pThis->hSpinlock);
696 pThis->hSpinlock = NIL_RTSPINLOCK;
697 RTMemFree(pThis);
698
699 NOREF(cRefs);
700
701 return true;
702}
703
704
705/**
706 * Releases a reference to the specified instance.
707 *
708 * This method will destroy the instance when the count reaches 0.
709 * It will also take care of decrementing the counter and idle wakeup.
710 *
711 * @param pThis The instance.
712 * @param fBusy Whether the busy counter should be decremented too.
713 */
714DECLHIDDEN(void) vboxNetFltRelease(PVBOXNETFLTINS pThis, bool fBusy)
715{
716 uint32_t cRefs;
717
718 /*
719 * Paranoid Android.
720 */
721 AssertPtr(pThis);
722 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
723 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
724 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
725 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
726 AssertPtr(pThis->pGlobals);
727 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
728 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
729 Assert(pThis->szName[0]);
730
731 /*
732 * Work the busy counter.
733 */
734 if (fBusy)
735 {
736 cRefs = ASMAtomicDecU32(&pThis->cBusy);
737 if (!cRefs)
738 {
739 int rc = RTSemEventSignal(pThis->hEventIdle);
740 AssertRC(rc);
741 }
742 else
743 Assert(cRefs < UINT32_MAX / 2);
744 }
745
746 /*
747 * The object reference counting.
748 */
749 cRefs = ASMAtomicDecU32(&pThis->cRefs);
750 if (!cRefs)
751 vboxNetFltDestroyInstance(pThis);
752 else
753 Assert(cRefs < UINT32_MAX / 2);
754}
755
756
757/**
758 * @copydoc INTNETTRUNKIFPORT::pfnRetain
759 */
760static DECLCALLBACK(void) vboxNetFltPortRelease(PINTNETTRUNKIFPORT pIfPort)
761{
762 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
763 vboxNetFltRelease(pThis, false /* fBusy */);
764}
765
766
767/**
768 * Retains a reference to the specified instance and a busy reference too.
769 *
770 * @param pThis The instance.
771 * @param fBusy Whether the busy counter should be incremented as well.
772 */
773DECLHIDDEN(void) vboxNetFltRetain(PVBOXNETFLTINS pThis, bool fBusy)
774{
775 uint32_t cRefs;
776
777 /*
778 * Paranoid Android.
779 */
780 AssertPtr(pThis);
781 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
782 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
783 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
784 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
785 AssertPtr(pThis->pGlobals);
786 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
787 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
788 Assert(pThis->szName[0]);
789
790 /*
791 * Retain the object.
792 */
793 cRefs = ASMAtomicIncU32(&pThis->cRefs);
794 Assert(cRefs > 1 && cRefs < UINT32_MAX / 2);
795
796 /*
797 * Work the busy counter.
798 */
799 if (fBusy)
800 {
801 cRefs = ASMAtomicIncU32(&pThis->cBusy);
802 Assert(cRefs > 0 && cRefs < UINT32_MAX / 2);
803 }
804
805 NOREF(cRefs);
806}
807
808
809/**
810 * Tries to retain the device as busy if the trunk is active.
811 *
812 * This is used before calling pfnRecv or pfnPreRecv.
813 *
814 * @returns true if we succeeded in retaining a busy reference to the active
815 * device. false if we failed.
816 * @param pThis The instance.
817 */
818DECLHIDDEN(bool) vboxNetFltTryRetainBusyActive(PVBOXNETFLTINS pThis)
819{
820 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
821 uint32_t cRefs;
822 bool fRc;
823
824 /*
825 * Paranoid Android.
826 */
827 AssertPtr(pThis);
828 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
829 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
830 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
831 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
832 AssertPtr(pThis->pGlobals);
833 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
834 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
835 Assert(pThis->szName[0]);
836
837 /*
838 * Do the retaining and checking behind the spinlock.
839 */
840 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
841 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE;
842 if (fRc)
843 {
844 cRefs = ASMAtomicIncU32(&pThis->cRefs);
845 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
846
847 cRefs = ASMAtomicIncU32(&pThis->cBusy);
848 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
849 }
850 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
851
852 return fRc;
853}
854
855
856/**
857 * Tries to retain the device as busy if the trunk is not disconnecting.
858 *
859 * This is used before reporting stuff to the internal network.
860 *
861 * @returns true if we succeeded in retaining a busy reference to the active
862 * device. false if we failed.
863 * @param pThis The instance.
864 */
865DECLHIDDEN(bool) vboxNetFltTryRetainBusyNotDisconnected(PVBOXNETFLTINS pThis)
866{
867 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
868 uint32_t cRefs;
869 bool fRc;
870
871 /*
872 * Paranoid Android.
873 */
874 AssertPtr(pThis);
875 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
876 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
877 Assert( vboxNetFltGetState(pThis) > kVBoxNetFltInsState_Invalid
878 && vboxNetFltGetState(pThis) < kVBoxNetFltInsState_Destroyed);
879 AssertPtr(pThis->pGlobals);
880 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
881 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
882 Assert(pThis->szName[0]);
883
884 /*
885 * Do the retaining and checking behind the spinlock.
886 */
887 RTSpinlockAcquireNoInts(pThis->hSpinlock, &Tmp);
888 fRc = pThis->enmTrunkState == INTNETTRUNKIFSTATE_ACTIVE
889 || pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE;
890 if (fRc)
891 {
892 cRefs = ASMAtomicIncU32(&pThis->cRefs);
893 AssertMsg(cRefs > 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
894
895 cRefs = ASMAtomicIncU32(&pThis->cBusy);
896 AssertMsg(cRefs >= 1 && cRefs < UINT32_MAX / 2, ("%d\n", cRefs)); NOREF(cRefs);
897 }
898 RTSpinlockReleaseNoInts(pThis->hSpinlock, &Tmp);
899
900 return fRc;
901}
902
903
904/**
905 * @copydoc INTNETTRUNKIFPORT::pfnRetain
906 */
907static DECLCALLBACK(void) vboxNetFltPortRetain(PINTNETTRUNKIFPORT pIfPort)
908{
909 PVBOXNETFLTINS pThis = IFPORT_2_VBOXNETFLTINS(pIfPort);
910 vboxNetFltRetain(pThis, false /* fBusy */);
911}
912
913
914/**
915 * Connects the instance to the specified switch port.
916 *
917 * Called while owning the lock. We're ASSUMING that the internal
918 * networking code is already owning an recursive mutex, so, there
919 * will be no deadlocks when vboxNetFltOsConnectIt calls back into
920 * it for setting preferences.
921 *
922 * @returns VBox status code.
923 * @param pThis The instance.
924 * @param pSwitchPort The port on the internal network 'switch'.
925 * @param ppIfPort Where to return our port interface.
926 */
927static int vboxNetFltConnectIt(PVBOXNETFLTINS pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
928{
929 int rc;
930
931 /*
932 * Validate state.
933 */
934 Assert(!pThis->fRediscoveryPending);
935 Assert(!pThis->cBusy);
936#ifdef VBOXNETFLT_STATIC_CONFIG
937 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Unconnected);
938#else
939 Assert(vboxNetFltGetState(pThis) == kVBoxNetFltInsState_Initializing);
940#endif
941 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
942
943 /*
944 * Do the job.
945 * Note that we're calling the os stuff while owning the semaphore here.
946 */
947 pThis->pSwitchPort = pSwitchPort;
948 rc = vboxNetFltOsConnectIt(pThis);
949 if (RT_SUCCESS(rc))
950 {
951 vboxNetFltSetState(pThis, kVBoxNetFltInsState_Connected);
952 *ppIfPort = &pThis->MyPort;
953 }
954 else
955 pThis->pSwitchPort = NULL;
956
957 Assert(pThis->enmTrunkState == INTNETTRUNKIFSTATE_INACTIVE);
958 return rc;
959}
960
961
962/**
963 * Creates a new instance.
964 *
965 * The new instance will be in the suspended state in a dynamic config and in
966 * the inactive in a static one.
967 *
968 * Called without owning the lock, but will request is several times.
969 *
970 * @returns VBox status code.
971 * @param pGlobals The globals.
972 * @param pszName The instance name.
973 * @param pSwitchPort The port on the switch that we're connected with (dynamic only).
974 * @param fNoPromisc Do not attempt going into promiscuous mode.
975 * @param pvContext Context argument for vboxNetFltOsInitInstance.
976 * @param ppIfPort Where to store the pointer to our port interface (dynamic only).
977 */
978static int vboxNetFltNewInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PINTNETTRUNKSWPORT pSwitchPort,
979 bool fNoPromisc, void *pvContext, PINTNETTRUNKIFPORT *ppIfPort)
980{
981 /*
982 * Allocate and initialize a new instance before requesting the mutex.
983 * Note! That in a static config we'll initialize the trunk state to
984 * disconnecting and flip it in vboxNetFltFactoryCreateAndConnect
985 * later on. This better reflext the state and it works better with
986 * assertions in the destruction path.
987 */
988 int rc;
989 size_t const cchName = strlen(pszName);
990 PVBOXNETFLTINS pNew = (PVBOXNETFLTINS)RTMemAllocZ(RT_OFFSETOF(VBOXNETFLTINS, szName[cchName + 1]));
991 if (!pNew)
992 return VERR_INTNET_FLT_IF_FAILED;
993 pNew->pNext = NULL;
994 pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
995 pNew->MyPort.pfnRetain = vboxNetFltPortRetain;
996 pNew->MyPort.pfnRelease = vboxNetFltPortRelease;
997 pNew->MyPort.pfnDisconnectAndRelease= vboxNetFltPortDisconnectAndRelease;
998 pNew->MyPort.pfnSetState = vboxNetFltPortSetState;
999 pNew->MyPort.pfnWaitForIdle = vboxNetFltPortWaitForIdle;
1000 pNew->MyPort.pfnXmit = vboxNetFltPortXmit;
1001 pNew->MyPort.pfnNotifyMacAddress = vboxNetFltPortNotifyMacAddress;
1002 pNew->MyPort.pfnConnectInterface = vboxNetFltPortConnectInterface;
1003 pNew->MyPort.pfnDisconnectInterface = vboxNetFltPortDisconnectInterface;
1004 pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
1005 pNew->pSwitchPort = pSwitchPort;
1006 pNew->pGlobals = pGlobals;
1007 pNew->hSpinlock = NIL_RTSPINLOCK;
1008 pNew->enmState = kVBoxNetFltInsState_Initializing;
1009#ifdef VBOXNETFLT_STATIC_CONFIG
1010 pNew->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
1011#else
1012 pNew->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE;
1013#endif
1014 pNew->fDisconnectedFromHost = false;
1015 pNew->fRediscoveryPending = false;
1016 pNew->fDisablePromiscuous = fNoPromisc;
1017 pNew->NanoTSLastRediscovery = INT64_MAX;
1018 pNew->cRefs = 1;
1019 pNew->cBusy = 0;
1020 pNew->hEventIdle = NIL_RTSEMEVENT;
1021 memcpy(pNew->szName, pszName, cchName + 1);
1022
1023 rc = RTSpinlockCreate(&pNew->hSpinlock);
1024 if (RT_SUCCESS(rc))
1025 {
1026 rc = RTSemEventCreate(&pNew->hEventIdle);
1027 if (RT_SUCCESS(rc))
1028 {
1029 rc = vboxNetFltOsPreInitInstance(pNew);
1030 if (RT_SUCCESS(rc))
1031 {
1032 /*
1033 * Insert the instance into the chain, checking for
1034 * duplicates first of course (race).
1035 */
1036 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1037 if (RT_SUCCESS(rc))
1038 {
1039 if (!vboxNetFltFindInstanceLocked(pGlobals, pszName))
1040 {
1041 pNew->pNext = pGlobals->pInstanceHead;
1042 pGlobals->pInstanceHead = pNew;
1043 RTSemFastMutexRelease(pGlobals->hFastMtx);
1044
1045 /*
1046 * Call the OS specific initialization code.
1047 */
1048 rc = vboxNetFltOsInitInstance(pNew, pvContext);
1049 RTSemFastMutexRequest(pGlobals->hFastMtx);
1050 if (RT_SUCCESS(rc))
1051 {
1052#ifdef VBOXNETFLT_STATIC_CONFIG
1053 /*
1054 * Static instances are unconnected at birth.
1055 */
1056 Assert(!pSwitchPort);
1057 pNew->enmState = kVBoxNetFltInsState_Unconnected;
1058 RTSemFastMutexRelease(pGlobals->hFastMtx);
1059 *ppIfPort = &pNew->MyPort;
1060 return rc;
1061
1062#else /* !VBOXNETFLT_STATIC_CONFIG */
1063 /*
1064 * Connect it as well, the OS specific bits has to be done outside
1065 * the lock as they may call back to into intnet.
1066 */
1067 rc = vboxNetFltConnectIt(pNew, pSwitchPort, ppIfPort);
1068 if (RT_SUCCESS(rc))
1069 {
1070 RTSemFastMutexRelease(pGlobals->hFastMtx);
1071 Assert(*ppIfPort == &pNew->MyPort);
1072 return rc;
1073 }
1074
1075 /* Bail out (failed). */
1076 vboxNetFltOsDeleteInstance(pNew);
1077#endif /* !VBOXNETFLT_STATIC_CONFIG */
1078 }
1079 vboxNetFltUnlinkLocked(pGlobals, pNew);
1080 }
1081 else
1082 rc = VERR_INTNET_FLT_IF_BUSY;
1083 RTSemFastMutexRelease(pGlobals->hFastMtx);
1084 }
1085 }
1086 RTSemEventDestroy(pNew->hEventIdle);
1087 }
1088 RTSpinlockDestroy(pNew->hSpinlock);
1089 }
1090
1091 RTMemFree(pNew);
1092 return rc;
1093}
1094
1095
1096#ifdef VBOXNETFLT_STATIC_CONFIG
1097/**
1098 * Searches for the NetFlt instance by its name and creates the new one if not found.
1099 *
1100 * @returns VBox status code.
1101 * @retval VINF_SUCCESS and *ppInstance if a new instance was created.
1102 * @retval VINF_ALREADY_INITIALIZED and *ppInstance if an instance already exists.
1103 *
1104 * @param pGlobal Pointer to the globals.
1105 * @param pszName The instance name.
1106 * @param ppInstance Where to return the instance pointer on success.
1107 * @param pvContext Context which needs to be passed along to vboxNetFltOsInitInstance.
1108 */
1109DECLHIDDEN(int) vboxNetFltSearchCreateInstance(PVBOXNETFLTGLOBALS pGlobals, const char *pszName, PVBOXNETFLTINS *ppInstance, void *pvContext)
1110{
1111 PINTNETTRUNKIFPORT pIfPort;
1112 PVBOXNETFLTINS pCur;
1113 VBOXNETFTLINSSTATE enmState;
1114 int rc;
1115
1116 *ppInstance = NULL;
1117 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1118 AssertRCReturn(rc, rc);
1119
1120 /*
1121 * Look for an existing instance in the list.
1122 *
1123 * There might be an existing one in the list if the driver was unbound
1124 * while it was connected to an internal network. We're running into
1125 * a destruction race that is a bit similar to the one in
1126 * vboxNetFltFactoryCreateAndConnect, only the roles are reversed
1127 * and we're not in a position to back down. Instead of backing down
1128 * we'll delay a bit giving the other thread time to complete the
1129 * destructor.
1130 */
1131 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1132 while (pCur)
1133 {
1134 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
1135 if (cRefs > 1)
1136 {
1137 enmState = vboxNetFltGetState(pCur);
1138 switch (enmState)
1139 {
1140 case kVBoxNetFltInsState_Unconnected:
1141 case kVBoxNetFltInsState_Connected:
1142 case kVBoxNetFltInsState_Disconnecting:
1143 if (pCur->fDisconnectedFromHost)
1144 {
1145 /* Wait for it to exit the transitional disconnecting
1146 state. It might otherwise be running the risk of
1147 upsetting the OS specific code... */
1148 /** @todo This reconnect stuff should be serialized correctly for static
1149 * devices. Shouldn't it? In the dynamic case we're using the INTNET
1150 * outbound thrunk lock, but that doesn't quite cut it here, or does
1151 * it? We could either transition to initializing or make a callback
1152 * while owning the mutext here... */
1153 if (enmState == kVBoxNetFltInsState_Disconnecting)
1154 {
1155 do
1156 {
1157 RTSemFastMutexRelease(pGlobals->hFastMtx);
1158 RTThreadSleep(2); /* (2ms) */
1159 RTSemFastMutexRequest(pGlobals->hFastMtx);
1160 enmState = vboxNetFltGetState(pCur);
1161 }
1162 while (enmState == kVBoxNetFltInsState_Disconnecting);
1163 AssertMsg(enmState == kVBoxNetFltInsState_Unconnected, ("%d\n", enmState));
1164 Assert(pCur->fDisconnectedFromHost);
1165 }
1166
1167 RTSemFastMutexRelease(pGlobals->hFastMtx);
1168 *ppInstance = pCur;
1169 return VINF_ALREADY_INITIALIZED;
1170 }
1171 /* fall thru */
1172
1173 default:
1174 {
1175 bool fDfH = pCur->fDisconnectedFromHost;
1176 RTSemFastMutexRelease(pGlobals->hFastMtx);
1177 vboxNetFltRelease(pCur, false /* fBusy */);
1178 LogRel(("VBoxNetFlt: Huh? An instance of '%s' already exists! [pCur=%p cRefs=%d fDfH=%RTbool enmState=%d]\n",
1179 pszName, pCur, cRefs - 1, fDfH, enmState));
1180 *ppInstance = NULL;
1181 return VERR_INTNET_FLT_IF_BUSY;
1182 }
1183 }
1184 }
1185
1186 /* Zero references, it's being destroyed. Delay a bit so the destructor
1187 can finish its work and try again. (vboxNetFltNewInstance will fail
1188 with duplicate name if we don't.) */
1189# ifdef RT_STRICT
1190 Assert(cRefs == 1);
1191 enmState = vboxNetFltGetState(pCur);
1192 AssertMsg( enmState == kVBoxNetFltInsState_Unconnected
1193 || enmState == kVBoxNetFltInsState_Disconnecting
1194 || enmState == kVBoxNetFltInsState_Destroyed, ("%d\n", enmState));
1195# endif
1196 ASMAtomicDecU32(&pCur->cRefs);
1197 RTSemFastMutexRelease(pGlobals->hFastMtx);
1198 RTThreadSleep(2); /* (2ms) */
1199 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1200 AssertRCReturn(rc, rc);
1201
1202 /* try again */
1203 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1204 }
1205
1206 RTSemFastMutexRelease(pGlobals->hFastMtx);
1207
1208 /*
1209 * Try create a new instance.
1210 * (fNoPromisc is overridden in the vboxNetFltFactoryCreateAndConnect path, so pass true here.)
1211 */
1212 rc = vboxNetFltNewInstance(pGlobals, pszName, NULL, true /* fNoPromisc */, pvContext, &pIfPort);
1213 if (RT_SUCCESS(rc))
1214 *ppInstance = IFPORT_2_VBOXNETFLTINS(pIfPort);
1215 else
1216 *ppInstance = NULL;
1217
1218 return rc;
1219}
1220#endif /* VBOXNETFLT_STATIC_CONFIG */
1221
1222
1223/**
1224 * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
1225 */
1226static DECLCALLBACK(int) vboxNetFltFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
1227 PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
1228 PINTNETTRUNKIFPORT *ppIfPort)
1229{
1230 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
1231 PVBOXNETFLTINS pCur;
1232 int rc;
1233
1234 LogFlow(("vboxNetFltFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
1235 Assert(pGlobals->cFactoryRefs > 0);
1236 AssertMsgReturn(!(fFlags & ~(INTNETTRUNKFACTORY_FLAG_NO_PROMISC)),
1237 ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
1238
1239 /*
1240 * Static: Find instance, check if busy, connect if not.
1241 * Dynamic: Check for duplicate / busy interface instance.
1242 */
1243 rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1244 AssertRCReturn(rc, rc);
1245
1246//#if defined(VBOXNETADP) && defined(RT_OS_WINDOWS)
1247// /* temporary hack to pick up the first adapter */
1248// pCur = pGlobals->pInstanceHead; /** @todo Don't for get to remove this temporary hack... :-) */
1249//#else
1250 pCur = vboxNetFltFindInstanceLocked(pGlobals, pszName);
1251//#endif
1252 if (pCur)
1253 {
1254#ifdef VBOXNETFLT_STATIC_CONFIG
1255 /* Try grab a reference. If the count had already reached zero we're racing the
1256 destructor code and must back down. */
1257 uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
1258 if (cRefs > 1)
1259 {
1260 if (vboxNetFltGetState(pCur) == kVBoxNetFltInsState_Unconnected)
1261 {
1262 pCur->enmTrunkState = INTNETTRUNKIFSTATE_INACTIVE; /** @todo protect me? */
1263 pCur->fDisablePromiscuous = !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC);
1264 rc = vboxNetFltConnectIt(pCur, pSwitchPort, ppIfPort);
1265 if (RT_SUCCESS(rc))
1266 pCur = NULL; /* Don't release it, reference given to the caller. */
1267 else
1268 pCur->enmTrunkState = INTNETTRUNKIFSTATE_DISCONNECTING;
1269 }
1270 else
1271 rc = VERR_INTNET_FLT_IF_BUSY;
1272 }
1273 else
1274 {
1275 Assert(cRefs == 1);
1276 ASMAtomicDecU32(&pCur->cRefs);
1277 pCur = NULL; /* nothing to release */
1278 rc = VERR_INTNET_FLT_IF_NOT_FOUND;
1279 }
1280
1281 RTSemFastMutexRelease(pGlobals->hFastMtx);
1282 if (pCur)
1283 vboxNetFltRelease(pCur, false /* fBusy */);
1284#else
1285 rc = VERR_INTNET_FLT_IF_BUSY;
1286 RTSemFastMutexRelease(pGlobals->hFastMtx);
1287#endif
1288 LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
1289 return rc;
1290 }
1291
1292 RTSemFastMutexRelease(pGlobals->hFastMtx);
1293
1294#ifdef VBOXNETFLT_STATIC_CONFIG
1295 rc = VERR_INTNET_FLT_IF_NOT_FOUND;
1296#else
1297 /*
1298 * Dynamically create a new instance.
1299 */
1300 rc = vboxNetFltNewInstance(pGlobals,
1301 pszName,
1302 pSwitchPort,
1303 !!(fFlags & INTNETTRUNKFACTORY_FLAG_NO_PROMISC),
1304 NULL,
1305 ppIfPort);
1306#endif
1307 LogFlow(("vboxNetFltFactoryCreateAndConnect: returns %Rrc\n", rc));
1308 return rc;
1309}
1310
1311
1312/**
1313 * @copydoc INTNETTRUNKFACTORY::pfnRelease
1314 */
1315static DECLCALLBACK(void) vboxNetFltFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
1316{
1317 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, TrunkFactory));
1318
1319 int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
1320 Assert(cRefs >= 0); NOREF(cRefs);
1321 LogFlow(("vboxNetFltFactoryRelease: cRefs=%d (new)\n", cRefs));
1322}
1323
1324
1325/**
1326 * Implements the SUPDRV component factor interface query method.
1327 *
1328 * @returns Pointer to an interface. NULL if not supported.
1329 *
1330 * @param pSupDrvFactory Pointer to the componet factory registration structure.
1331 * @param pSession The session - unused.
1332 * @param pszInterfaceUuid The factory interface id.
1333 */
1334static DECLCALLBACK(void *) vboxNetFltQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid)
1335{
1336 PVBOXNETFLTGLOBALS pGlobals = (PVBOXNETFLTGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXNETFLTGLOBALS, SupDrvFactory));
1337
1338 /*
1339 * Convert the UUID strings and compare them.
1340 */
1341 RTUUID UuidReq;
1342 int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
1343 if (RT_SUCCESS(rc))
1344 {
1345 if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR))
1346 {
1347 ASMAtomicIncS32(&pGlobals->cFactoryRefs);
1348 return &pGlobals->TrunkFactory;
1349 }
1350#ifdef LOG_ENABLED
1351 /* log legacy queries */
1352 /* else if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_V1_UUID_STR))
1353 Log(("VBoxNetFlt: V1 factory query\n"));
1354 */
1355 else
1356 Log(("VBoxNetFlt: unknown factory interface query (%s)\n", pszInterfaceUuid));
1357#endif
1358 }
1359 else
1360 Log(("VBoxNetFlt: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
1361
1362 return NULL;
1363}
1364
1365
1366/**
1367 * Checks whether the VBoxNetFlt wossname can be unloaded.
1368 *
1369 * This will return false if someone is currently using the module.
1370 *
1371 * @returns true if it's relatively safe to unload it, otherwise false.
1372 * @param pGlobals Pointer to the globals.
1373 */
1374DECLHIDDEN(bool) vboxNetFltCanUnload(PVBOXNETFLTGLOBALS pGlobals)
1375{
1376 int rc = RTSemFastMutexRequest(pGlobals->hFastMtx);
1377 bool fRc = !pGlobals->pInstanceHead
1378 && pGlobals->cFactoryRefs <= 0;
1379 RTSemFastMutexRelease(pGlobals->hFastMtx);
1380 AssertRC(rc);
1381 return fRc;
1382}
1383
1384
1385/**
1386 * Try to close the IDC connection to SUPDRV if established.
1387 *
1388 * @returns VBox status code.
1389 * @retval VINF_SUCCESS on success.
1390 * @retval VERR_WRONG_ORDER if we're busy.
1391 *
1392 * @param pGlobals Pointer to the globals.
1393 *
1394 * @sa vboxNetFltTryDeleteIdcAndGlobals()
1395 */
1396DECLHIDDEN(int) vboxNetFltTryDeleteIdc(PVBOXNETFLTGLOBALS pGlobals)
1397{
1398 int rc;
1399
1400 Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
1401
1402 /*
1403 * Check before trying to deregister the factory.
1404 */
1405 if (!vboxNetFltCanUnload(pGlobals))
1406 return VERR_WRONG_ORDER;
1407
1408 if (!pGlobals->fIDCOpen)
1409 rc = VINF_SUCCESS;
1410 else
1411 {
1412 /*
1413 * Disconnect from SUPDRV and check that nobody raced us,
1414 * reconnect if that should happen.
1415 */
1416 rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1417 AssertRC(rc);
1418 if (!vboxNetFltCanUnload(pGlobals))
1419 {
1420 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1421 AssertRC(rc);
1422 return VERR_WRONG_ORDER;
1423 }
1424
1425 SUPR0IdcClose(&pGlobals->SupDrvIDC);
1426 pGlobals->fIDCOpen = false;
1427 }
1428
1429 return rc;
1430}
1431
1432
1433/**
1434 * Establishes the IDC connection to SUPDRV and registers our component factory.
1435 *
1436 * @returns VBox status code.
1437 * @param pGlobals Pointer to the globals.
1438 * @sa vboxNetFltInitGlobalsAndIdc().
1439 */
1440DECLHIDDEN(int) vboxNetFltInitIdc(PVBOXNETFLTGLOBALS pGlobals)
1441{
1442 int rc;
1443 Assert(!pGlobals->fIDCOpen);
1444
1445 /*
1446 * Establish a connection to SUPDRV and register our component factory.
1447 */
1448 rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
1449 if (RT_SUCCESS(rc))
1450 {
1451 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1452 if (RT_SUCCESS(rc))
1453 {
1454 pGlobals->fIDCOpen = true;
1455 Log(("VBoxNetFlt: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
1456 return rc;
1457 }
1458
1459 /* bail out. */
1460 LogRel(("VBoxNetFlt: Failed to register component factory, rc=%Rrc\n", rc));
1461 SUPR0IdcClose(&pGlobals->SupDrvIDC);
1462 }
1463
1464 return rc;
1465}
1466
1467
1468/**
1469 * Deletes the globals.
1470 *
1471 * This must be called after the IDC connection has been closed,
1472 * see vboxNetFltTryDeleteIdc().
1473 *
1474 * @param pGlobals Pointer to the globals.
1475 * @sa vboxNetFltTryDeleteIdcAndGlobals()
1476 */
1477DECLHIDDEN(void) vboxNetFltDeleteGlobals(PVBOXNETFLTGLOBALS pGlobals)
1478{
1479 Assert(!pGlobals->fIDCOpen);
1480
1481 /*
1482 * Release resources.
1483 */
1484 RTSemFastMutexDestroy(pGlobals->hFastMtx);
1485 pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
1486}
1487
1488
1489/**
1490 * Initializes the globals.
1491 *
1492 * @returns VBox status code.
1493 * @param pGlobals Pointer to the globals.
1494 * @sa vboxNetFltInitGlobalsAndIdc().
1495 */
1496DECLHIDDEN(int) vboxNetFltInitGlobals(PVBOXNETFLTGLOBALS pGlobals)
1497{
1498 /*
1499 * Initialize the common portions of the structure.
1500 */
1501 int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
1502 if (RT_SUCCESS(rc))
1503 {
1504 pGlobals->pInstanceHead = NULL;
1505
1506 pGlobals->TrunkFactory.pfnRelease = vboxNetFltFactoryRelease;
1507 pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetFltFactoryCreateAndConnect;
1508#if defined(RT_OS_WINDOWS) && defined(VBOXNETADP)
1509 memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetAdp", sizeof("VBoxNetAdp"));
1510#else
1511 memcpy(pGlobals->SupDrvFactory.szName, "VBoxNetFlt", sizeof("VBoxNetFlt"));
1512#endif
1513 pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetFltQueryFactoryInterface;
1514 pGlobals->fIDCOpen = false;
1515
1516 return rc;
1517 }
1518
1519 return rc;
1520}
1521
1522
1523/**
1524 * Called by the native part when the OS wants the driver to unload.
1525 *
1526 * @returns VINF_SUCCESS on success, VERR_WRONG_ORDER if we're busy.
1527 *
1528 * @param pGlobals Pointer to the globals.
1529 */
1530DECLHIDDEN(int) vboxNetFltTryDeleteIdcAndGlobals(PVBOXNETFLTGLOBALS pGlobals)
1531{
1532 int rc = vboxNetFltTryDeleteIdc(pGlobals);
1533 if (RT_SUCCESS(rc))
1534 vboxNetFltDeleteGlobals(pGlobals);
1535 return rc;
1536}
1537
1538
1539/**
1540 * Called by the native driver/kext module initialization routine.
1541 *
1542 * It will initialize the common parts of the globals, assuming the caller
1543 * has already taken care of the OS specific bits, and establish the IDC
1544 * connection to SUPDRV.
1545 *
1546 * @returns VBox status code.
1547 * @param pGlobals Pointer to the globals.
1548 */
1549DECLHIDDEN(int) vboxNetFltInitGlobalsAndIdc(PVBOXNETFLTGLOBALS pGlobals)
1550{
1551 /*
1552 * Initialize the common portions of the structure.
1553 */
1554 int rc = vboxNetFltInitGlobals(pGlobals);
1555 if (RT_SUCCESS(rc))
1556 {
1557 rc = vboxNetFltInitIdc(pGlobals);
1558 if (RT_SUCCESS(rc))
1559 return rc;
1560
1561 /* bail out. */
1562 vboxNetFltDeleteGlobals(pGlobals);
1563 }
1564
1565 return rc;
1566}
1567
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