VirtualBox

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

Last change on this file since 67954 was 62708, checked in by vboxsync, 8 years ago

HostDrivers: warnings

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

© 2023 Oracle
ContactPrivacy policyTerms of Use