VirtualBox

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

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

NetAdp/win: make the comment-to adapter name to be honored in the driver, make MAC address to be loaded/stored from/to the registry

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