VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetAdp/VBoxNetAdp.c@ 28666

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

SrvIntNet,VBoxNetFlt: Changing from getting to reporting (VBoxNetFlt reports stuff to SrvIntNet).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.2 KB
Line 
1/* $Id: VBoxNetAdp.c 28666 2010-04-23 17:25:58Z vboxsync $ */
2/** @file
3 * VBoxNetAdp - Virtual Network Adapter Driver (Host), Common Code.
4 */
5
6/*
7 * Copyright (C) 2008-2009 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22/** @page pg_netadp VBoxNetAdp - Network Adapter
23 *
24 * This is a kernel module that creates a virtual interface that can be attached
25 * to an internal network.
26 *
27 * In the big picture we're one of the three trunk interface on the internal
28 * network, the one named "TAP Interface": @image html Networking_Overview.gif
29 *
30 */
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
36#include "VBoxNetAdpInternal.h"
37
38#include <VBox/log.h>
39#include <VBox/err.h>
40#include <iprt/string.h>
41
42#ifdef VBOXANETADP_DO_NOT_USE_NETFLT
43
44#include <VBox/sup.h>
45#include <iprt/assert.h>
46#include <iprt/spinlock.h>
47#include <iprt/uuid.h>
48#include <VBox/version.h>
49
50/** r=bird: why is this here in the agnositc code? */
51#ifdef RT_OS_DARWIN
52# include <net/ethernet.h>
53# include <net/if_ether.h>
54# include <net/if_types.h>
55# include <sys/socket.h>
56# include <net/if.h>
57# include <net/if_dl.h>
58# include <sys/errno.h>
59# include <sys/param.h>
60#endif
61
62
63/*******************************************************************************
64* Defined Constants And Macros *
65*******************************************************************************/
66#define IFPORT_2_VBOXNETADP(pIfPort) \
67 ( (PVBOXNETADP)((uint8_t *)pIfPort - RT_OFFSETOF(VBOXNETADP, MyPort)) )
68
69
70AssertCompileMemberSize(VBOXNETADP, enmState, sizeof(uint32_t));
71
72/**
73 * Gets the enmState member atomically.
74 *
75 * Used for all reads.
76 *
77 * @returns The enmState value.
78 * @param pThis The instance.
79 */
80DECLINLINE(VBOXNETADPSTATE) vboxNetAdpGetState(PVBOXNETADP pThis)
81{
82 return (VBOXNETADPSTATE)ASMAtomicUoReadU32((uint32_t volatile *)&pThis->enmState);
83}
84
85
86/**
87 * Sets the enmState member atomically.
88 *
89 * Used for all updates.
90 *
91 * @param pThis The instance.
92 * @param enmNewState The new value.
93 */
94DECLINLINE(void) vboxNetAdpSetState(PVBOXNETADP pThis, VBOXNETADPSTATE enmNewState)
95{
96 Log(("vboxNetAdpSetState: pThis=%p, state change: %d -> %d.\n", pThis, vboxNetAdpGetState(pThis), enmNewState));
97 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, enmNewState);
98}
99
100
101/**
102 * Sets the enmState member atomically after first acquiring the spinlock.
103 *
104 * Used for all updates.
105 *
106 * @param pThis The instance.
107 * @param enmNewState The new value.
108 */
109DECLINLINE(void) vboxNetAdpSetStateWithLock(PVBOXNETADP pThis, VBOXNETADPSTATE enmNewState)
110{
111 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
112 Log(("vboxNetAdpSetStateWithLock: pThis=%p, state=%d.\n", pThis, enmNewState));
113 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
114 vboxNetAdpSetState(pThis, enmNewState);
115 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
116}
117
118
119/**
120 * Gets the enmState member with locking.
121 *
122 * Used for all reads.
123 *
124 * @returns The enmState value.
125 * @param pThis The instance.
126 */
127DECLINLINE(VBOXNETADPSTATE) vboxNetAdpGetStateWithLock(PVBOXNETADP pThis)
128{
129 VBOXNETADPSTATE enmState;
130 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
131 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
132 enmState = vboxNetAdpGetState(pThis);
133 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
134 Log(("vboxNetAdpGetStateWithLock: pThis=%p, state=%d.\n", pThis, enmState));
135 return enmState;
136}
137
138
139/**
140 * Checks and sets the enmState member atomically.
141 *
142 * Used for all updates.
143 *
144 * @returns true if the state has been changed.
145 * @param pThis The instance.
146 * @param enmNewState The new value.
147 */
148DECLINLINE(bool) vboxNetAdpCheckAndSetState(PVBOXNETADP pThis, VBOXNETADPSTATE enmOldState, VBOXNETADPSTATE enmNewState)
149{
150 VBOXNETADPSTATE enmActualState;
151 bool fRc = true; /* be optimistic */
152 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
153
154 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
155 enmActualState = vboxNetAdpGetState(pThis); /** @todo r=bird: ASMAtomicCmpXchgU32()*/
156 if (enmActualState == enmOldState)
157 vboxNetAdpSetState(pThis, enmNewState);
158 else
159 fRc = false;
160 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
161
162 if (fRc)
163 Log(("vboxNetAdpCheckAndSetState: pThis=%p, state changed: %d -> %d.\n", pThis, enmOldState, enmNewState));
164 else
165 Log(("vboxNetAdpCheckAndSetState: pThis=%p, no state change: %d != %d (expected).\n", pThis, enmActualState, enmOldState));
166 return fRc;
167}
168
169
170/**
171 * Finds a instance by its name, the caller does the locking.
172 *
173 * @returns Pointer to the instance by the given name. NULL if not found.
174 * @param pGlobals The globals.
175 * @param pszName The name of the instance.
176 */
177static PVBOXNETADP vboxNetAdpFind(PVBOXNETADPGLOBALS pGlobals, const char *pszName)
178{
179 unsigned i;
180
181 for (i = 0; i < RT_ELEMENTS(pGlobals->aAdapters); i++)
182 {
183 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
184 PVBOXNETADP pThis = &pGlobals->aAdapters[i];
185 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
186 if ( vboxNetAdpGetState(pThis)
187 && !strcmp(pThis->szName, pszName))
188 {
189 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
190 return pThis;
191 }
192 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
193 }
194 return NULL;
195}
196
197
198/**
199 * Releases a reference to the specified instance.
200 *
201 * @param pThis The instance.
202 * @param fBusy Whether the busy counter should be decremented too.
203 */
204DECLHIDDEN(void) vboxNetAdpRelease(PVBOXNETADP pThis)
205{
206 uint32_t cRefs;
207
208 /*
209 * Paranoid Android.
210 */
211 AssertPtr(pThis);
212 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
213 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
214 Assert(vboxNetAdpGetState(pThis) > kVBoxNetAdpState_Invalid);
215 AssertPtr(pThis->pGlobals);
216 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
217 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
218 Assert(pThis->szName[0]);
219
220 /*
221 * The object reference counting.
222 */
223 cRefs = ASMAtomicDecU32(&pThis->cRefs);
224 Assert(cRefs < UINT32_MAX / 2);
225}
226
227
228/**
229 * Decrements the busy counter and does idle wakeup.
230 *
231 * @param pThis The instance.
232 */
233DECLHIDDEN(void) vboxNetAdpIdle(PVBOXNETADP pThis)
234{
235 uint32_t cBusy;
236
237 /*
238 * Paranoid Android.
239 */
240 AssertPtr(pThis);
241 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
242 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
243 Assert(vboxNetAdpGetState(pThis) >= kVBoxNetAdpState_Connected);
244 AssertPtr(pThis->pGlobals);
245 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
246
247 cBusy = ASMAtomicDecU32(&pThis->cBusy);
248 if (!cBusy)
249 {
250 int rc = RTSemEventSignal(pThis->hEventIdle);
251 AssertRC(rc);
252 }
253 else
254 Assert(cBusy < UINT32_MAX / 2);
255}
256
257
258/**
259 * Retains a reference to the specified instance.
260 *
261 * @param pThis The instance.
262 */
263DECLHIDDEN(void) vboxNetAdpRetain(PVBOXNETADP pThis)
264{
265 uint32_t cRefs;
266
267 /*
268 * Paranoid Android.
269 */
270 AssertPtr(pThis);
271 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
272 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
273 Assert(vboxNetAdpGetState(pThis) > kVBoxNetAdpState_Invalid);
274 AssertPtr(pThis->pGlobals);
275 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
276 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
277 Assert(pThis->szName[0]);
278
279 /*
280 * Retain the object.
281 */
282 cRefs = ASMAtomicIncU32(&pThis->cRefs);
283 Assert(cRefs > 1 && cRefs < UINT32_MAX / 2);
284
285 NOREF(cRefs);
286}
287
288
289/**
290 * Increments busy counter.
291 *
292 * @param pThis The instance.
293 */
294DECLHIDDEN(void) vboxNetAdpBusy(PVBOXNETADP pThis)
295{
296 uint32_t cBusy;
297
298 /*
299 * Are we vigilant enough?
300 */
301 AssertPtr(pThis);
302 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
303 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
304 Assert(vboxNetAdpGetState(pThis) >= kVBoxNetAdpState_Connected);
305 AssertPtr(pThis->pGlobals);
306 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
307 cBusy = ASMAtomicIncU32(&pThis->cBusy);
308 Assert(cBusy > 0 && cBusy < UINT32_MAX / 2);
309
310 NOREF(cBusy);
311}
312
313
314/**
315 * Generate a suitable MAC address.
316 *
317 * @param pThis The instance.
318 * @param pMac Where to return the MAC address.
319 */
320DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac)
321{
322#if 0 /* Use a locally administered version of the OUI we use for the guest NICs. */
323 pMac->au8[0] = 0x08 | 2;
324 pMac->au8[1] = 0x00;
325 pMac->au8[2] = 0x27;
326#else /* this is what \0vb comes down to. It seems to be unassigned atm. */
327 pMac->au8[0] = 0;
328 pMac->au8[1] = 0x76;
329 pMac->au8[2] = 0x62;
330#endif
331
332 pMac->au8[3] = 0; /* pThis->uUnit >> 16; */
333 pMac->au8[4] = 0; /* pThis->uUnit >> 8; */
334 pMac->au8[5] = pThis->uUnit;
335}
336
337
338/**
339 * Checks if receive is possible and increases busy and ref counters if so.
340 *
341 * @param pThis The instance.
342 */
343DECLHIDDEN(bool) vboxNetAdpPrepareToReceive(PVBOXNETADP pThis)
344{
345 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
346 bool fCanReceive = false;
347 /*
348 * Input validation.
349 */
350 AssertPtr(pThis);
351 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
352 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
353 if (vboxNetAdpGetState(pThis) == kVBoxNetAdpState_Active)
354 {
355 fCanReceive = true;
356 vboxNetAdpRetain(pThis);
357 vboxNetAdpBusy(pThis);
358 }
359 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
360 Log(("vboxNetAdpPrepareToReceive: fCanReceive=%d.\n", fCanReceive));
361
362 return fCanReceive;
363}
364
365
366/**
367 * Forwards scatter/gather list to internal network and decreases busy and ref counters.
368 *
369 * @param pThis The instance.
370 */
371DECLHIDDEN(void) vboxNetAdpReceive(PVBOXNETADP pThis, PINTNETSG pSG)
372{
373 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
374 /*
375 * Input validation.
376 */
377 AssertPtr(pThis);
378 AssertPtr(pSG);
379 AssertPtr(pThis->pSwitchPort);
380 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
381 Log(("vboxNetAdpReceive: forwarding packet to internal net...\n"));
382 pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, pSG, INTNETTRUNKDIR_HOST);
383 vboxNetAdpIdle(pThis);
384 vboxNetAdpRelease(pThis);
385}
386
387
388/**
389 * Decreases busy and ref counters.
390 *
391 * @param pThis The instance.
392 */
393DECLHIDDEN(void) vboxNetAdpCancelReceive(PVBOXNETADP pThis)
394{
395 Log(("vboxNetAdpCancelReceive: cancelled.\n"));
396 vboxNetAdpIdle(pThis);
397 vboxNetAdpRelease(pThis);
398}
399
400
401/**
402 * @copydoc INTNETTRUNKIFPORT::pfnXmit
403 */
404static DECLCALLBACK(int) vboxNetAdpPortXmit(PINTNETTRUNKIFPORT pIfPort, PINTNETSG pSG, uint32_t fDst)
405{
406 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
407 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
408 int rc = VINF_SUCCESS;
409
410 /*
411 * Input validation.
412 */
413 AssertPtr(pThis);
414 AssertPtr(pSG);
415 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
416
417 Log(("vboxNetAdpPortXmit: outgoing packet (len=%d)\n", pSG->cbTotal));
418
419 /*
420 * Do a retain/busy, invoke the OS specific code.
421 */
422 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
423 if (vboxNetAdpGetState(pThis) != kVBoxNetAdpState_Active)
424 {
425 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
426 Log(("vboxNetAdpReceive: Dropping incoming packet for inactive interface %s.\n",
427 pThis->szName));
428 return VERR_INVALID_STATE;
429 }
430 vboxNetAdpRetain(pThis);
431 vboxNetAdpBusy(pThis);
432 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
433
434 rc = vboxNetAdpPortOsXmit(pThis, pSG, fDst);
435 vboxNetAdpIdle(pThis);
436 vboxNetAdpRelease(pThis);
437
438 return rc;
439}
440
441
442/**
443 * @copydoc INTNETTRUNKIFPORT::pfnGetMacAddress
444 */
445static DECLCALLBACK(void) vboxNetAdpPortGetMacAddress(PINTNETTRUNKIFPORT pIfPort, PRTMAC pMac)
446{
447 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
448
449 /*
450 * Input validation.
451 */
452 AssertPtr(pThis);
453 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
454 Assert(vboxNetAdpGetStateWithLock(pThis) == kVBoxNetAdpState_Active);
455
456 /*
457 * Forward the question to the OS specific code.
458 */
459 vboxNetAdpPortOsGetMacAddress(pThis, pMac);
460}
461
462
463/**
464 * @copydoc INTNETTRUNKIFPORT::pfnWaitForIdle
465 */
466static DECLCALLBACK(int) vboxNetAdpPortWaitForIdle(PINTNETTRUNKIFPORT pIfPort, uint32_t cMillies)
467{
468 int rc;
469 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
470
471 /*
472 * Input validation.
473 */
474 AssertPtr(pThis);
475 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
476 AssertReturn(vboxNetAdpGetStateWithLock(pThis) >= kVBoxNetAdpState_Connected, VERR_INVALID_STATE);
477
478 /*
479 * Go to sleep on the semaphore after checking the busy count.
480 */
481 vboxNetAdpRetain(pThis);
482
483 rc = VINF_SUCCESS;
484 while (pThis->cBusy && RT_SUCCESS(rc))
485 rc = RTSemEventWait(pThis->hEventIdle, cMillies); /** @todo make interruptible? */
486
487 vboxNetAdpRelease(pThis);
488
489 return rc;
490}
491
492
493/**
494 * @copydoc INTNETTRUNKIFPORT::pfnSetActive
495 */
496static DECLCALLBACK(bool) vboxNetAdpPortSetActive(PINTNETTRUNKIFPORT pIfPort, bool fActive)
497{
498 bool fPreviouslyActive;
499 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
500 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
501
502 /*
503 * Input validation.
504 */
505 AssertPtr(pThis);
506 AssertPtr(pThis->pGlobals);
507 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
508
509 Log(("vboxNetAdpPortSetActive: pThis=%p, fActive=%d, state before: %d.\n", pThis, fActive, vboxNetAdpGetState(pThis)));
510 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
511
512 fPreviouslyActive = vboxNetAdpGetState(pThis) == kVBoxNetAdpState_Active;
513 if (fPreviouslyActive != fActive)
514 {
515 switch (vboxNetAdpGetState(pThis))
516 {
517 case kVBoxNetAdpState_Connected:
518 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Active);
519 break;
520 case kVBoxNetAdpState_Active:
521 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Connected);
522 break;
523 default:
524 break;
525 }
526 }
527
528 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
529 Log(("vboxNetAdpPortSetActive: state after: %RTbool.\n", vboxNetAdpGetState(pThis)));
530 return fPreviouslyActive;
531}
532
533
534/**
535 * @copydoc INTNETTRUNKIFPORT::pfnDisconnectAndRelease
536 */
537static DECLCALLBACK(void) vboxNetAdpPortDisconnectAndRelease(PINTNETTRUNKIFPORT pIfPort)
538{
539 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
540 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
541
542 /*
543 * Serious paranoia.
544 */
545 AssertPtr(pThis);
546 Assert(pThis->MyPort.u32Version == INTNETTRUNKIFPORT_VERSION);
547 Assert(pThis->MyPort.u32VersionEnd == INTNETTRUNKIFPORT_VERSION);
548 AssertPtr(pThis->pGlobals);
549 Assert(pThis->hEventIdle != NIL_RTSEMEVENT);
550 Assert(pThis->hSpinlock != NIL_RTSPINLOCK);
551
552
553 /*
554 * Disconnect and release it.
555 */
556 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
557 //Assert(vboxNetAdpGetState(pThis) == kVBoxNetAdpState_Connected);
558 Assert(!pThis->cBusy);
559 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Transitional);
560 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
561
562 vboxNetAdpOsDisconnectIt(pThis);
563 pThis->pSwitchPort = NULL;
564
565 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
566 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Available);
567 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
568
569 vboxNetAdpRelease(pThis);
570}
571
572
573/**
574 * @copydoc INTNETTRUNKIFPORT::pfnRelease
575 */
576static DECLCALLBACK(void) vboxNetAdpPortRelease(PINTNETTRUNKIFPORT pIfPort)
577{
578 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
579 vboxNetAdpRelease(pThis);
580}
581
582
583/**
584 * @copydoc INTNETTRUNKIFPORT::pfnRetain
585 */
586static DECLCALLBACK(void) vboxNetAdpPortRetain(PINTNETTRUNKIFPORT pIfPort)
587{
588 PVBOXNETADP pThis = IFPORT_2_VBOXNETADP(pIfPort);
589 vboxNetAdpRetain(pThis);
590}
591
592
593int vboxNetAdpCreate (PINTNETTRUNKFACTORY pIfFactory, PVBOXNETADP *ppNew)
594{
595 int rc;
596 unsigned i;
597 PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, TrunkFactory));
598
599 for (i = 0; i < RT_ELEMENTS(pGlobals->aAdapters); i++)
600 {
601 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
602 PVBOXNETADP pThis = &pGlobals->aAdapters[i];
603
604 if (vboxNetAdpCheckAndSetState(pThis, kVBoxNetAdpState_Invalid, kVBoxNetAdpState_Transitional))
605 {
606 /* Found an empty slot -- use it. */
607 RTMAC Mac;
608 Assert(ASMAtomicIncU32(&pThis->cRefs) == 1);
609 vboxNetAdpComposeMACAddress(pThis, &Mac);
610 rc = vboxNetAdpOsCreate(pThis, &Mac);
611 *ppNew = pThis;
612 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
613 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Available);
614 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
615 return rc;
616 }
617 }
618
619 /* All slots in adapter array are busy. */
620 return VERR_OUT_OF_RESOURCES;
621}
622
623int vboxNetAdpDestroy (PVBOXNETADP pThis)
624{
625 int rc = VINF_SUCCESS;
626 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
627
628 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
629 if (vboxNetAdpGetState(pThis) != kVBoxNetAdpState_Available || pThis->cBusy)
630 {
631 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
632 return VERR_INTNET_FLT_IF_BUSY;
633 }
634 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Transitional);
635 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
636 vboxNetAdpRelease(pThis);
637
638 vboxNetAdpOsDestroy(pThis);
639
640 RTSpinlockAcquire(pThis->hSpinlock, &Tmp);
641 vboxNetAdpSetState(pThis, kVBoxNetAdpState_Invalid);
642 RTSpinlockRelease(pThis->hSpinlock, &Tmp);
643
644 return rc;
645}
646
647/**
648 * Connects the instance to the specified switch port.
649 *
650 * Called while owning the lock. We're ASSUMING that the internal
651 * networking code is already owning an recursive mutex, so, there
652 * will be no deadlocks when vboxNetAdpOsConnectIt calls back into
653 * it for setting preferences.
654 *
655 * @returns VBox status code.
656 * @param pThis The instance.
657 * @param pSwitchPort The port on the internal network 'switch'.
658 * @param ppIfPort Where to return our port interface.
659 */
660static int vboxNetAdpConnectIt(PVBOXNETADP pThis, PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT *ppIfPort)
661{
662 int rc;
663
664 /*
665 * Validate state.
666 */
667 Assert(!pThis->cBusy);
668 Assert(vboxNetAdpGetStateWithLock(pThis) == kVBoxNetAdpState_Transitional);
669
670 /*
671 * Do the job.
672 * Note that we're calling the os stuff while owning the semaphore here.
673 */
674 pThis->pSwitchPort = pSwitchPort;
675 rc = vboxNetAdpOsConnectIt(pThis);
676 if (RT_SUCCESS(rc))
677 {
678 *ppIfPort = &pThis->MyPort;
679 }
680 else
681 pThis->pSwitchPort = NULL;
682
683 return rc;
684}
685
686
687/**
688 * @copydoc INTNETTRUNKFACTORY::pfnCreateAndConnect
689 */
690static DECLCALLBACK(int) vboxNetAdpFactoryCreateAndConnect(PINTNETTRUNKFACTORY pIfFactory, const char *pszName,
691 PINTNETTRUNKSWPORT pSwitchPort, uint32_t fFlags,
692 PINTNETTRUNKIFPORT *ppIfPort)
693{
694 PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, TrunkFactory));
695 PVBOXNETADP pThis;
696 int rc;
697
698 LogFlow(("vboxNetAdpFactoryCreateAndConnect: pszName=%p:{%s} fFlags=%#x\n", pszName, pszName, fFlags));
699 Assert(pGlobals->cFactoryRefs > 0);
700 AssertMsgReturn(!fFlags,
701 ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
702
703 /*
704 * Find instance, check if busy, connect if not.
705 */
706 pThis = vboxNetAdpFind(pGlobals, pszName);
707 if (pThis)
708 {
709 if (vboxNetAdpCheckAndSetState(pThis, kVBoxNetAdpState_Available, kVBoxNetAdpState_Transitional))
710 {
711 vboxNetAdpRetain(pThis);
712 rc = vboxNetAdpConnectIt(pThis, pSwitchPort, ppIfPort);
713 vboxNetAdpSetStateWithLock(pThis, RT_SUCCESS(rc) ? kVBoxNetAdpState_Connected : kVBoxNetAdpState_Available);
714 }
715 else
716 rc = VERR_INTNET_FLT_IF_BUSY;
717 }
718 else
719 rc = VERR_INTNET_FLT_IF_NOT_FOUND;
720
721 return rc;
722}
723
724
725/**
726 * @copydoc INTNETTRUNKFACTORY::pfnRelease
727 */
728static DECLCALLBACK(void) vboxNetAdpFactoryRelease(PINTNETTRUNKFACTORY pIfFactory)
729{
730 PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pIfFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, TrunkFactory));
731
732 int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs);
733 Assert(cRefs >= 0); NOREF(cRefs);
734 LogFlow(("vboxNetAdpFactoryRelease: cRefs=%d (new)\n", cRefs));
735}
736
737
738/**
739 * Implements the SUPDRV component factor interface query method.
740 *
741 * @returns Pointer to an interface. NULL if not supported.
742 *
743 * @param pSupDrvFactory Pointer to the component factory registration structure.
744 * @param pSession The session - unused.
745 * @param pszInterfaceUuid The factory interface id.
746 */
747static DECLCALLBACK(void *) vboxNetAdpQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid)
748{
749 PVBOXNETADPGLOBALS pGlobals = (PVBOXNETADPGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXNETADPGLOBALS, SupDrvFactory));
750
751 /*
752 * Convert the UUID strings and compare them.
753 */
754 RTUUID UuidReq;
755 int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid);
756 if (RT_SUCCESS(rc))
757 {
758 if (!RTUuidCompareStr(&UuidReq, INTNETTRUNKFACTORY_UUID_STR))
759 {
760 ASMAtomicIncS32(&pGlobals->cFactoryRefs);
761 return &pGlobals->TrunkFactory;
762 }
763#ifdef LOG_ENABLED
764 else
765 Log(("VBoxNetAdp: unknown factory interface query (%s)\n", pszInterfaceUuid));
766#endif
767 }
768 else
769 Log(("VBoxNetAdp: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid));
770
771 return NULL;
772}
773
774
775/**
776 * Checks whether the VBoxNetAdp wossname can be unloaded.
777 *
778 * This will return false if someone is currently using the module.
779 *
780 * @returns true if it's relatively safe to unload it, otherwise false.
781 * @param pGlobals Pointer to the globals.
782 */
783DECLHIDDEN(bool) vboxNetAdpCanUnload(PVBOXNETADPGLOBALS pGlobals)
784{
785 bool fRc = true; /* Assume it can be unloaded. */
786 unsigned i;
787
788 for (i = 0; i < RT_ELEMENTS(pGlobals->aAdapters); i++)
789 {
790 PVBOXNETADP pThis = &pGlobals->aAdapters[i];
791 if (vboxNetAdpGetStateWithLock(&pGlobals->aAdapters[i]) >= kVBoxNetAdpState_Connected)
792 {
793 fRc = false;
794 break; /* We already know the answer. */
795 }
796 }
797 return fRc && ASMAtomicUoReadS32((int32_t volatile *)&pGlobals->cFactoryRefs) <= 0;
798}
799
800/**
801 * tries to deinitialize Idc
802 * we separate the globals settings "base" which is actually
803 * "general" globals settings except for Idc, and idc.
804 * This is needed for windows filter driver, which gets loaded prior to VBoxDrv,
805 * thus it's not possible to make idc initialization from the driver startup routine for it,
806 * though the "base is still needed for the driver to functions".
807 * @param pGlobals
808 * @return VINF_SUCCESS on succes, VERR_WRONG_ORDER if we're busy.
809 */
810DECLHIDDEN(int) vboxNetAdpTryDeleteIdc(PVBOXNETADPGLOBALS pGlobals)
811{
812 int rc;
813
814 Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX);
815
816 /*
817 * Check before trying to deregister the factory.
818 */
819 if (!vboxNetAdpCanUnload(pGlobals))
820 return VERR_WRONG_ORDER;
821
822 /*
823 * Disconnect from SUPDRV and check that nobody raced us,
824 * reconnect if that should happen.
825 */
826 rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
827 AssertRC(rc);
828 if (!vboxNetAdpCanUnload(pGlobals))
829 {
830 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
831 AssertRC(rc);
832 return VERR_WRONG_ORDER;
833 }
834
835 SUPR0IdcClose(&pGlobals->SupDrvIDC);
836
837 return rc;
838}
839
840static int vboxNetAdpSlotCreate(PVBOXNETADPGLOBALS pGlobals, unsigned uUnit, PVBOXNETADP pNew)
841{
842 int rc;
843
844 pNew->MyPort.u32Version = INTNETTRUNKIFPORT_VERSION;
845 pNew->MyPort.pfnRetain = vboxNetAdpPortRetain;
846 pNew->MyPort.pfnRelease = vboxNetAdpPortRelease;
847 pNew->MyPort.pfnDisconnectAndRelease= vboxNetAdpPortDisconnectAndRelease;
848 pNew->MyPort.pfnSetActive = vboxNetAdpPortSetActive;
849 pNew->MyPort.pfnWaitForIdle = vboxNetAdpPortWaitForIdle;
850 pNew->MyPort.pfnGetMacAddress = vboxNetAdpPortGetMacAddress;
851 pNew->MyPort.pfnXmit = vboxNetAdpPortXmit;
852 pNew->MyPort.u32VersionEnd = INTNETTRUNKIFPORT_VERSION;
853 pNew->pSwitchPort = NULL;
854 pNew->pGlobals = pGlobals;
855 pNew->hSpinlock = NIL_RTSPINLOCK;
856 pNew->enmState = kVBoxNetAdpState_Invalid;
857 pNew->cRefs = 0;
858 pNew->cBusy = 0;
859 pNew->hEventIdle = NIL_RTSEMEVENT;
860
861 rc = RTSpinlockCreate(&pNew->hSpinlock);
862 if (RT_SUCCESS(rc))
863 {
864 rc = RTSemEventCreate(&pNew->hEventIdle);
865 if (RT_SUCCESS(rc))
866 {
867 rc = vboxNetAdpOsInit(pNew);
868 if (RT_SUCCESS(rc))
869 {
870 return rc;
871 }
872 RTSemEventDestroy(pNew->hEventIdle);
873 pNew->hEventIdle = NIL_RTSEMEVENT;
874 }
875 RTSpinlockDestroy(pNew->hSpinlock);
876 pNew->hSpinlock = NIL_RTSPINLOCK;
877 }
878 return rc;
879}
880
881static void vboxNetAdpSlotDestroy(PVBOXNETADP pThis)
882{
883 Assert(pThis->cRefs == 0);
884 Assert(pThis->cBusy == 0);
885 Assert(vboxNetAdpGetState(pThis) == kVBoxNetAdpState_Invalid);
886 if (pThis->hEventIdle != NIL_RTSEMEVENT)
887 {
888 RTSemEventDestroy(pThis->hEventIdle);
889 pThis->hEventIdle = NIL_RTSEMEVENT;
890 }
891 if (pThis->hSpinlock != NIL_RTSPINLOCK)
892 {
893 RTSpinlockDestroy(pThis->hSpinlock);
894 pThis->hSpinlock = NIL_RTSPINLOCK;
895 }
896}
897
898/**
899 * performs "base" globals deinitialization
900 * we separate the globals settings "base" which is actually
901 * "general" globals settings except for Idc, and idc.
902 * This is needed for windows filter driver, which gets loaded prior to VBoxDrv,
903 * thus it's not possible to make idc initialization from the driver startup routine for it,
904 * though the "base is still needed for the driver to functions".
905 * @param pGlobals
906 * @return none
907 */
908DECLHIDDEN(void) vboxNetAdpDeleteGlobalsBase(PVBOXNETADPGLOBALS pGlobals)
909{
910 int i;
911 /*
912 * Release resources.
913 */
914 for (i = 0; i < (int)RT_ELEMENTS(pGlobals->aAdapters); i++)
915 if (RT_SUCCESS(vboxNetAdpDestroy(&pGlobals->aAdapters[i])))
916 vboxNetAdpSlotDestroy(&pGlobals->aAdapters[i]);
917
918 RTSemFastMutexDestroy(pGlobals->hFastMtx);
919 pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX;
920
921#ifdef VBOXNETADP_STATIC_CONFIG
922 RTSemEventDestroy(pGlobals->hTimerEvent);
923 pGlobals->hTimerEvent = NIL_RTSEMEVENT;
924#endif
925
926}
927
928
929/**
930 * Called by the native part when the OS wants the driver to unload.
931 *
932 * @returns VINF_SUCCESS on succes, VERR_WRONG_ORDER if we're busy.
933 *
934 * @param pGlobals Pointer to the globals.
935 */
936DECLHIDDEN(int) vboxNetAdpTryDeleteGlobals(PVBOXNETADPGLOBALS pGlobals)
937{
938 int rc = vboxNetAdpTryDeleteIdc(pGlobals);
939 if (RT_SUCCESS(rc))
940 {
941 vboxNetAdpDeleteGlobalsBase(pGlobals);
942 }
943 return rc;
944}
945
946
947/**
948 * performs the "base" globals initialization
949 * we separate the globals initialization to globals "base" initialization which is actually
950 * "general" globals initialization except for Idc not being initialized, and idc initialization.
951 * This is needed for windows filter driver, which gets loaded prior to VBoxDrv,
952 * thus it's not possible to make idc initialization from the driver startup routine for it.
953 *
954 * @returns VBox status code.
955 * @param pGlobals Pointer to the globals. */
956DECLHIDDEN(int) vboxNetAdpInitGlobalsBase(PVBOXNETADPGLOBALS pGlobals)
957{
958 /*
959 * Initialize the common portions of the structure.
960 */
961 int i;
962 int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx);
963 if (RT_SUCCESS(rc))
964 {
965 memset(pGlobals->aAdapters, 0, sizeof(pGlobals->aAdapters));
966 for (i = 0; i < (int)RT_ELEMENTS(pGlobals->aAdapters); i++)
967 {
968 rc = vboxNetAdpSlotCreate(pGlobals, i, &pGlobals->aAdapters[i]);
969 if (RT_FAILURE(rc))
970 {
971 /* Clean up. */
972 while (--i >= 0)
973 vboxNetAdpSlotDestroy(&pGlobals->aAdapters[i]);
974 Log(("vboxNetAdpInitGlobalsBase: Failed to create fast mutex (rc=%Rrc).\n", rc));
975 RTSemFastMutexDestroy(pGlobals->hFastMtx);
976 return rc;
977 }
978 }
979 pGlobals->TrunkFactory.pfnRelease = vboxNetAdpFactoryRelease;
980 pGlobals->TrunkFactory.pfnCreateAndConnect = vboxNetAdpFactoryCreateAndConnect;
981
982 strcpy(pGlobals->SupDrvFactory.szName, "VBoxNetAdp");
983 pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxNetAdpQueryFactoryInterface;
984 }
985
986 return rc;
987}
988
989/**
990 * performs the Idc initialization
991 * we separate the globals initialization to globals "base" initialization which is actually
992 * "general" globals initialization except for Idc not being initialized, and idc initialization.
993 * This is needed for windows filter driver, which gets loaded prior to VBoxDrv,
994 * thus it's not possible to make idc initialization from the driver startup routine for it.
995 *
996 * @returns VBox status code.
997 * @param pGlobals Pointer to the globals. */
998DECLHIDDEN(int) vboxNetAdpInitIdc(PVBOXNETADPGLOBALS pGlobals)
999{
1000 int rc;
1001 /*
1002 * Establish a connection to SUPDRV and register our component factory.
1003 */
1004 rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL);
1005 if (RT_SUCCESS(rc))
1006 {
1007 rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory);
1008 if (RT_SUCCESS(rc))
1009 {
1010#if 1 /** @todo REMOVE ME! */
1011 PVBOXNETADP pTmp;
1012 rc = vboxNetAdpCreate(&pGlobals->TrunkFactory, &pTmp);
1013 if (RT_FAILURE(rc))
1014 Log(("Failed to create vboxnet0, rc=%Rrc.\n", rc));
1015#endif
1016 Log(("VBoxNetAdp: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC)));
1017 return rc;
1018 }
1019
1020 /* bail out. */
1021 LogRel(("VBoxNetAdp: Failed to register component factory, rc=%Rrc\n", rc));
1022 SUPR0IdcClose(&pGlobals->SupDrvIDC);
1023 }
1024
1025 return rc;
1026}
1027
1028/**
1029 * Called by the native driver/kext module initialization routine.
1030 *
1031 * It will initialize the common parts of the globals, assuming the caller
1032 * has already taken care of the OS specific bits.
1033 *
1034 * @returns VBox status code.
1035 * @param pGlobals Pointer to the globals.
1036 */
1037DECLHIDDEN(int) vboxNetAdpInitGlobals(PVBOXNETADPGLOBALS pGlobals)
1038{
1039 /*
1040 * Initialize the common portions of the structure.
1041 */
1042 int rc = vboxNetAdpInitGlobalsBase(pGlobals);
1043 if (RT_SUCCESS(rc))
1044 {
1045 rc = vboxNetAdpInitIdc(pGlobals);
1046 if (RT_SUCCESS(rc))
1047 {
1048 return rc;
1049 }
1050
1051 /* bail out. */
1052 vboxNetAdpDeleteGlobalsBase(pGlobals);
1053 }
1054
1055 return rc;
1056}
1057
1058#else /* !VBOXANETADP_DO_NOT_USE_NETFLT */
1059
1060
1061VBOXNETADP g_aAdapters[VBOXNETADP_MAX_INSTANCES];
1062
1063
1064
1065/**
1066 * Generate a suitable MAC address.
1067 *
1068 * @param pThis The instance.
1069 * @param pMac Where to return the MAC address.
1070 */
1071DECLHIDDEN(void) vboxNetAdpComposeMACAddress(PVBOXNETADP pThis, PRTMAC pMac)
1072{
1073 /* Use a locally administered version of the OUI we use for the guest NICs. */
1074 pMac->au8[0] = 0x08 | 2;
1075 pMac->au8[1] = 0x00;
1076 pMac->au8[2] = 0x27;
1077
1078 pMac->au8[3] = 0; /* pThis->uUnit >> 16; */
1079 pMac->au8[4] = 0; /* pThis->uUnit >> 8; */
1080 pMac->au8[5] = pThis->uUnit;
1081}
1082
1083int vboxNetAdpCreate (PVBOXNETADP *ppNew)
1084{
1085 int rc;
1086 unsigned i;
1087 for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
1088 {
1089 PVBOXNETADP pThis = &g_aAdapters[i];
1090
1091 if (ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Invalid))
1092 {
1093 RTMAC Mac;
1094 /* Found an empty slot -- use it. */
1095 Log(("vboxNetAdpCreate: found empty slot: %d\n", i));
1096 vboxNetAdpComposeMACAddress(pThis, &Mac);
1097 rc = vboxNetAdpOsCreate(pThis, &Mac);
1098 Log(("vboxNetAdpCreate: pThis=%p pThis->szName=%p\n", pThis, pThis->szName));
1099 if (RT_SUCCESS(rc))
1100 {
1101 *ppNew = pThis;
1102 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Active);
1103 }
1104 else
1105 {
1106 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
1107 Log(("vboxNetAdpCreate: vboxNetAdpOsCreate failed with '%Rrc'.\n", rc));
1108 }
1109 Log2(("VBoxNetAdpCreate: Created %s\n", g_aAdapters[i].szName));
1110 for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
1111 Log2(("VBoxNetAdpCreate: Scanning entry: state=%d name=%s\n", g_aAdapters[i].enmState, g_aAdapters[i].szName));
1112 return rc;
1113 }
1114 }
1115 Log(("vboxNetAdpCreate: no empty slots!\n"));
1116
1117 /* All slots in adapter array are busy. */
1118 return VERR_OUT_OF_RESOURCES;
1119}
1120
1121int vboxNetAdpDestroy (PVBOXNETADP pThis)
1122{
1123 int rc = VINF_SUCCESS;
1124
1125 if (!ASMAtomicCmpXchgU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Transitional, kVBoxNetAdpState_Active))
1126 return VERR_INTNET_FLT_IF_BUSY;
1127
1128 vboxNetAdpOsDestroy(pThis);
1129
1130 ASMAtomicWriteU32((uint32_t volatile *)&pThis->enmState, kVBoxNetAdpState_Invalid);
1131
1132 return rc;
1133}
1134
1135int vboxNetAdpInit(void)
1136{
1137 unsigned i;
1138 PVBOXNETADP pVboxnet0;
1139 /*
1140 * Init common members and call OS-specific init.
1141 */
1142 for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
1143 {
1144 g_aAdapters[i].enmState = kVBoxNetAdpState_Invalid;
1145 g_aAdapters[i].uUnit = i;
1146 vboxNetAdpOsInit(&g_aAdapters[i]);
1147 }
1148
1149 /* Create vboxnet0 */
1150 return vboxNetAdpCreate(&pVboxnet0);
1151}
1152
1153/**
1154 * Finds an adapter by its name.
1155 *
1156 * @returns Pointer to the instance by the given name. NULL if not found.
1157 * @param pGlobals The globals.
1158 * @param pszName The name of the instance.
1159 */
1160PVBOXNETADP vboxNetAdpFindByName(const char *pszName)
1161{
1162 unsigned i;
1163
1164 for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
1165 {
1166 PVBOXNETADP pThis = &g_aAdapters[i];
1167 Log2(("VBoxNetAdp: Scanning entry: state=%d name=%s\n", pThis->enmState, pThis->szName));
1168 if ( strcmp(pThis->szName, pszName) == 0
1169 && ASMAtomicReadU32((uint32_t volatile *)&pThis->enmState) == kVBoxNetAdpState_Active)
1170 return pThis;
1171 }
1172 return NULL;
1173}
1174
1175void vboxNetAdpShutdown(void)
1176{
1177 unsigned i;
1178
1179 /* Remove virtual adapters */
1180 for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
1181 vboxNetAdpDestroy(&g_aAdapters[i]);
1182}
1183#endif /* !VBOXANETADP_DO_NOT_USE_NETFLT */
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