VirtualBox

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

Last change on this file since 96586 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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