VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvIntNet.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.3 KB
Line 
1/* $Id: DrvIntNet.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DrvIntNet - Internal network transport driver.
4 */
5
6/*
7 * Copyright (C) 2006-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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_INTNET
33#include <VBox/vmm/pdmdrv.h>
34#include <VBox/vmm/pdmnetinline.h>
35#include <VBox/vmm/pdmnetifs.h>
36#include <VBox/vmm/cfgm.h>
37#include <VBox/intnet.h>
38#include <VBox/intnetinline.h>
39#include <VBox/vmm/vmm.h>
40#include <VBox/sup.h>
41#include <VBox/err.h>
42
43#include <VBox/param.h>
44#include <VBox/log.h>
45#include <iprt/asm.h>
46#include <iprt/assert.h>
47#include <iprt/ctype.h>
48#include <iprt/memcache.h>
49#include <iprt/net.h>
50#include <iprt/semaphore.h>
51#include <iprt/string.h>
52#include <iprt/time.h>
53#include <iprt/thread.h>
54#include <iprt/uuid.h>
55#if defined(RT_OS_DARWIN) && defined(IN_RING3)
56# include <iprt/system.h>
57#endif
58
59#include "VBoxDD.h"
60
61
62/*********************************************************************************************************************************
63* Defined Constants And Macros *
64*********************************************************************************************************************************/
65#if 0
66/** Enables the ring-0 part. */
67#define VBOX_WITH_DRVINTNET_IN_R0
68#endif
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/**
75 * The state of the asynchronous thread.
76 */
77typedef enum RECVSTATE
78{
79 /** The thread is suspended. */
80 RECVSTATE_SUSPENDED = 1,
81 /** The thread is running. */
82 RECVSTATE_RUNNING,
83 /** The thread must (/has) terminate. */
84 RECVSTATE_TERMINATE,
85 /** The usual 32-bit type blowup. */
86 RECVSTATE_32BIT_HACK = 0x7fffffff
87} RECVSTATE;
88
89/**
90 * Internal networking driver instance data.
91 *
92 * @implements PDMINETWORKUP
93 */
94typedef struct DRVINTNET
95{
96 /** The network interface. */
97 PDMINETWORKUP INetworkUpR3;
98 /** The network interface. */
99 R3PTRTYPE(PPDMINETWORKDOWN) pIAboveNet;
100 /** The network config interface.
101 * Can (in theory at least) be NULL. */
102 R3PTRTYPE(PPDMINETWORKCONFIG) pIAboveConfigR3;
103 /** Pointer to the driver instance (ring-3). */
104 PPDMDRVINSR3 pDrvInsR3;
105 /** Pointer to the communication buffer (ring-3). */
106 R3PTRTYPE(PINTNETBUF) pBufR3;
107#ifdef VBOX_WITH_DRVINTNET_IN_R0
108 /** Ring-3 base interface for the ring-0 context. */
109 PDMIBASER0 IBaseR0;
110 /** Ring-3 base interface for the raw-mode context. */
111 PDMIBASERC IBaseRC;
112 RTR3PTR R3PtrAlignment;
113
114 /** The network interface for the ring-0 context. */
115 PDMINETWORKUPR0 INetworkUpR0;
116 /** Pointer to the driver instance (ring-0). */
117 PPDMDRVINSR0 pDrvInsR0;
118 /** Pointer to the communication buffer (ring-0). */
119 R0PTRTYPE(PINTNETBUF) pBufR0;
120
121 /** The network interface for the raw-mode context. */
122 PDMINETWORKUPRC INetworkUpRC;
123 /** Pointer to the driver instance. */
124 PPDMDRVINSRC pDrvInsRC;
125 RTRCPTR RCPtrAlignment;
126#endif
127
128 /** The transmit lock. */
129 PDMCRITSECT XmitLock;
130 /** Interface handle. */
131 INTNETIFHANDLE hIf;
132 /** The receive thread state. */
133 RECVSTATE volatile enmRecvState;
134 /** The receive thread. */
135 RTTHREAD hRecvThread;
136 /** The event semaphore that the receive thread waits on. */
137 RTSEMEVENT hRecvEvt;
138 /** The transmit thread. */
139 PPDMTHREAD pXmitThread;
140 /** The event semaphore that the transmit thread waits on. */
141 SUPSEMEVENT hXmitEvt;
142 /** The support driver session handle. */
143 PSUPDRVSESSION pSupDrvSession;
144 /** Scatter/gather descriptor cache. */
145 RTMEMCACHE hSgCache;
146 /** Set if the link is down.
147 * When the link is down all incoming packets will be dropped. */
148 bool volatile fLinkDown;
149 /** Set when the xmit thread has been signalled. (atomic) */
150 bool volatile fXmitSignalled;
151 /** Set if the transmit thread the one busy transmitting. */
152 bool volatile fXmitOnXmitThread;
153 /** The xmit thread should process the ring ASAP. */
154 bool fXmitProcessRing;
155 /** Set if data transmission should start immediately and deactivate
156 * as late as possible. */
157 bool fActivateEarlyDeactivateLate;
158 /** Padding. */
159 bool afReserved[HC_ARCH_BITS == 64 ? 3 : 3];
160 /** Scratch space for holding the ring-0 scatter / gather descriptor.
161 * The PDMSCATTERGATHER::fFlags member is used to indicate whether it is in
162 * use or not. Always accessed while owning the XmitLock. */
163 union
164 {
165 PDMSCATTERGATHER Sg;
166 uint8_t padding[8 * sizeof(RTUINTPTR)];
167 } u;
168 /** The network name. */
169 char szNetwork[INTNET_MAX_NETWORK_NAME];
170
171 /** Number of GSO packets sent. */
172 STAMCOUNTER StatSentGso;
173 /** Number of GSO packets received. */
174 STAMCOUNTER StatReceivedGso;
175 /** Number of packets send from ring-0. */
176 STAMCOUNTER StatSentR0;
177 /** The number of times we've had to wake up the xmit thread to continue the
178 * ring-0 job. */
179 STAMCOUNTER StatXmitWakeupR0;
180 /** The number of times we've had to wake up the xmit thread to continue the
181 * ring-3 job. */
182 STAMCOUNTER StatXmitWakeupR3;
183 /** The times the xmit thread has been told to process the ring. */
184 STAMCOUNTER StatXmitProcessRing;
185#ifdef VBOX_WITH_STATISTICS
186 /** Profiling packet transmit runs. */
187 STAMPROFILE StatTransmit;
188 /** Profiling packet receive runs. */
189 STAMPROFILEADV StatReceive;
190#endif /* VBOX_WITH_STATISTICS */
191#ifdef LOG_ENABLED
192 /** The nano ts of the last transfer. */
193 uint64_t u64LastTransferTS;
194 /** The nano ts of the last receive. */
195 uint64_t u64LastReceiveTS;
196#endif
197} DRVINTNET;
198AssertCompileMemberAlignment(DRVINTNET, XmitLock, 8);
199AssertCompileMemberAlignment(DRVINTNET, StatSentGso, 8);
200/** Pointer to instance data of the internal networking driver. */
201typedef DRVINTNET *PDRVINTNET;
202
203/**
204 * Config value to flag translation structure.
205 */
206typedef struct DRVINTNETFLAG
207{
208 /** The value. */
209 const char *pszChoice;
210 /** The corresponding flag. */
211 uint32_t fFlag;
212} DRVINTNETFLAG;
213/** Pointer to a const flag value translation. */
214typedef DRVINTNETFLAG const *PCDRVINTNETFLAG;
215
216
217#ifdef IN_RING3
218
219
220/**
221 * Updates the MAC address on the kernel side.
222 *
223 * @returns VBox status code.
224 * @param pThis The driver instance.
225 */
226static int drvR3IntNetUpdateMacAddress(PDRVINTNET pThis)
227{
228 if (!pThis->pIAboveConfigR3)
229 return VINF_SUCCESS;
230
231 INTNETIFSETMACADDRESSREQ SetMacAddressReq;
232 SetMacAddressReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
233 SetMacAddressReq.Hdr.cbReq = sizeof(SetMacAddressReq);
234 SetMacAddressReq.pSession = NIL_RTR0PTR;
235 SetMacAddressReq.hIf = pThis->hIf;
236 int rc = pThis->pIAboveConfigR3->pfnGetMac(pThis->pIAboveConfigR3, &SetMacAddressReq.Mac);
237 if (RT_SUCCESS(rc))
238 rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SET_MAC_ADDRESS,
239 &SetMacAddressReq, sizeof(SetMacAddressReq));
240
241 Log(("drvR3IntNetUpdateMacAddress: %.*Rhxs rc=%Rrc\n", sizeof(SetMacAddressReq.Mac), &SetMacAddressReq.Mac, rc));
242 return rc;
243}
244
245
246/**
247 * Sets the kernel interface active or inactive.
248 *
249 * Worker for poweron, poweroff, suspend and resume.
250 *
251 * @returns VBox status code.
252 * @param pThis The driver instance.
253 * @param fActive The new state.
254 */
255static int drvR3IntNetSetActive(PDRVINTNET pThis, bool fActive)
256{
257 if (!pThis->pIAboveConfigR3)
258 return VINF_SUCCESS;
259
260 INTNETIFSETACTIVEREQ SetActiveReq;
261 SetActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
262 SetActiveReq.Hdr.cbReq = sizeof(SetActiveReq);
263 SetActiveReq.pSession = NIL_RTR0PTR;
264 SetActiveReq.hIf = pThis->hIf;
265 SetActiveReq.fActive = fActive;
266 int rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SET_ACTIVE,
267 &SetActiveReq, sizeof(SetActiveReq));
268
269 Log(("drvR3IntNetSetActive: fActive=%d rc=%Rrc\n", fActive, rc));
270 AssertRC(rc);
271 return rc;
272}
273
274#endif /* IN_RING3 */
275
276/* -=-=-=-=- PDMINETWORKUP -=-=-=-=- */
277
278#ifndef IN_RING3
279/**
280 * Helper for signalling the xmit thread.
281 *
282 * @returns VERR_TRY_AGAIN (convenience).
283 * @param pThis The instance data..
284 */
285DECLINLINE(int) drvR0IntNetSignalXmit(PDRVINTNET pThis)
286{
287 /// @todo if (!ASMAtomicXchgBool(&pThis->fXmitSignalled, true)) - needs careful optimizing.
288 {
289 int rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
290 AssertRC(rc);
291 STAM_REL_COUNTER_INC(&pThis->CTX_SUFF(StatXmitWakeup));
292 }
293 return VERR_TRY_AGAIN;
294}
295#endif /* !IN_RING3 */
296
297
298/**
299 * Helper for processing the ring-0 consumer side of the xmit ring.
300 *
301 * The caller MUST own the xmit lock.
302 *
303 * @returns Status code from IntNetR0IfSend, except for VERR_TRY_AGAIN.
304 * @param pThis The instance data..
305 */
306DECLINLINE(int) drvIntNetProcessXmit(PDRVINTNET pThis)
307{
308 Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
309
310#ifdef IN_RING3
311 INTNETIFSENDREQ SendReq;
312 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
313 SendReq.Hdr.cbReq = sizeof(SendReq);
314 SendReq.pSession = NIL_RTR0PTR;
315 SendReq.hIf = pThis->hIf;
316 int rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
317#else
318 int rc = IntNetR0IfSend(pThis->hIf, pThis->pSupDrvSession);
319 if (rc == VERR_TRY_AGAIN)
320 {
321 ASMAtomicUoWriteBool(&pThis->fXmitProcessRing, true);
322 drvR0IntNetSignalXmit(pThis);
323 rc = VINF_SUCCESS;
324 }
325#endif
326 return rc;
327}
328
329
330
331/**
332 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
333 */
334PDMBOTHCBDECL(int) drvIntNetUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
335{
336 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
337#ifndef IN_RING3
338 Assert(!fOnWorkerThread);
339#endif
340
341 int rc = PDMDrvHlpCritSectTryEnter(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
342 if (RT_SUCCESS(rc))
343 {
344 if (fOnWorkerThread)
345 {
346 ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, true);
347 ASMAtomicWriteBool(&pThis->fXmitSignalled, false);
348 }
349 }
350 else if (rc == VERR_SEM_BUSY)
351 {
352 /** @todo Does this actually make sense if the other dude is an EMT and so
353 * forth? I seriously think this is ring-0 only...
354 * We might end up waking up the xmit thread unnecessarily here, even when in
355 * ring-0... This needs some more thought and optimizations when the ring-0 bits
356 * are working. */
357#ifdef IN_RING3
358 if ( !fOnWorkerThread
359 /*&& !ASMAtomicUoReadBool(&pThis->fXmitOnXmitThread)
360 && ASMAtomicCmpXchgBool(&pThis->fXmitSignalled, true, false)*/)
361 {
362 rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
363 AssertRC(rc);
364 }
365 rc = VERR_TRY_AGAIN;
366#else /* IN_RING0 */
367 rc = drvR0IntNetSignalXmit(pThis);
368#endif /* IN_RING0 */
369 }
370 return rc;
371}
372
373
374/**
375 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
376 */
377PDMBOTHCBDECL(int) drvIntNetUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
378 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
379{
380 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
381 int rc = VINF_SUCCESS;
382 Assert(cbMin < UINT32_MAX / 2);
383 Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
384
385 /*
386 * Allocate a S/G descriptor.
387 * This shouldn't normally fail as the NICs usually won't allocate more
388 * than one buffer at a time and the SG gets freed on sending.
389 */
390#ifdef IN_RING3
391 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemCacheAlloc(pThis->hSgCache);
392 if (!pSgBuf)
393 return VERR_NO_MEMORY;
394#else
395 PPDMSCATTERGATHER pSgBuf = &pThis->u.Sg;
396 if (RT_UNLIKELY(pSgBuf->fFlags != 0))
397 return drvR0IntNetSignalXmit(pThis);
398#endif
399
400 /*
401 * Allocate room in the ring buffer.
402 *
403 * In ring-3 we may have to process the xmit ring before there is
404 * sufficient buffer space since we might have stacked up a few frames to the
405 * trunk while in ring-0. (There is not point of doing this in ring-0.)
406 */
407 PINTNETHDR pHdr = NULL; /* gcc silliness */
408 if (pGso)
409 rc = IntNetRingAllocateGsoFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin, pGso,
410 &pHdr, &pSgBuf->aSegs[0].pvSeg);
411 else
412 rc = IntNetRingAllocateFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin,
413 &pHdr, &pSgBuf->aSegs[0].pvSeg);
414#ifdef IN_RING3
415 if ( RT_FAILURE(rc)
416 && pThis->CTX_SUFF(pBuf)->cbSend >= cbMin * 2 + sizeof(INTNETHDR))
417 {
418 drvIntNetProcessXmit(pThis);
419 if (pGso)
420 rc = IntNetRingAllocateGsoFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin, pGso,
421 &pHdr, &pSgBuf->aSegs[0].pvSeg);
422 else
423 rc = IntNetRingAllocateFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin,
424 &pHdr, &pSgBuf->aSegs[0].pvSeg);
425 }
426#endif
427 if (RT_SUCCESS(rc))
428 {
429 /*
430 * Set up the S/G descriptor and return successfully.
431 */
432 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
433 pSgBuf->cbUsed = 0;
434 pSgBuf->cbAvailable = cbMin;
435 pSgBuf->pvAllocator = pHdr;
436 pSgBuf->pvUser = pGso ? (PPDMNETWORKGSO)pSgBuf->aSegs[0].pvSeg - 1 : NULL;
437 pSgBuf->cSegs = 1;
438 pSgBuf->aSegs[0].cbSeg = cbMin;
439
440 *ppSgBuf = pSgBuf;
441 return VINF_SUCCESS;
442 }
443
444#ifdef IN_RING3
445 /*
446 * If the above fails, then we're really out of space. There are nobody
447 * competing with us here because of the xmit lock.
448 */
449 rc = VERR_NO_MEMORY;
450 RTMemCacheFree(pThis->hSgCache, pSgBuf);
451
452#else /* IN_RING0 */
453 /*
454 * If the request is reasonable, kick the xmit thread and tell it to
455 * process the xmit ring ASAP.
456 */
457 if (pThis->CTX_SUFF(pBuf)->cbSend >= cbMin * 2 + sizeof(INTNETHDR))
458 {
459 pThis->fXmitProcessRing = true;
460 rc = drvR0IntNetSignalXmit(pThis);
461 }
462 else
463 rc = VERR_NO_MEMORY;
464 pSgBuf->fFlags = 0;
465#endif /* IN_RING0 */
466 return rc;
467}
468
469
470/**
471 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
472 */
473PDMBOTHCBDECL(int) drvIntNetUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
474{
475 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
476 PINTNETHDR pHdr = (PINTNETHDR)pSgBuf->pvAllocator;
477#ifdef IN_RING0
478 Assert(pSgBuf == &pThis->u.Sg);
479#endif
480 Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
481 Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
482 Assert( pHdr->u8Type == INTNETHDR_TYPE_FRAME
483 || pHdr->u8Type == INTNETHDR_TYPE_GSO);
484 Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
485
486 /** @todo LATER: try unalloc the frame. */
487 pHdr->u8Type = INTNETHDR_TYPE_PADDING;
488 IntNetRingCommitFrame(&pThis->CTX_SUFF(pBuf)->Send, pHdr);
489
490#ifdef IN_RING3
491 RTMemCacheFree(pThis->hSgCache, pSgBuf);
492#else
493 pSgBuf->fFlags = 0;
494#endif
495 return VINF_SUCCESS;
496}
497
498
499/**
500 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
501 */
502PDMBOTHCBDECL(int) drvIntNetUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
503{
504 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
505 STAM_PROFILE_START(&pThis->StatTransmit, a);
506 RT_NOREF_PV(fOnWorkerThread);
507
508 AssertPtr(pSgBuf);
509 Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
510 Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
511 Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
512
513 if (pSgBuf->pvUser)
514 STAM_COUNTER_INC(&pThis->StatSentGso);
515
516 /*
517 * Commit the frame and push it thru the switch.
518 */
519 PINTNETHDR pHdr = (PINTNETHDR)pSgBuf->pvAllocator;
520 IntNetRingCommitFrameEx(&pThis->CTX_SUFF(pBuf)->Send, pHdr, pSgBuf->cbUsed);
521 int rc = drvIntNetProcessXmit(pThis);
522 STAM_PROFILE_STOP(&pThis->StatTransmit, a);
523
524 /*
525 * Free the descriptor and return.
526 */
527#ifdef IN_RING3
528 RTMemCacheFree(pThis->hSgCache, pSgBuf);
529#else
530 STAM_REL_COUNTER_INC(&pThis->StatSentR0);
531 pSgBuf->fFlags = 0;
532#endif
533 return rc;
534}
535
536
537/**
538 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
539 */
540PDMBOTHCBDECL(void) drvIntNetUp_EndXmit(PPDMINETWORKUP pInterface)
541{
542 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
543 ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, false);
544 PDMDrvHlpCritSectLeave(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
545}
546
547
548/**
549 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
550 */
551PDMBOTHCBDECL(void) drvIntNetUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
552{
553 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
554
555#ifdef IN_RING3
556 INTNETIFSETPROMISCUOUSMODEREQ Req;
557 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
558 Req.Hdr.cbReq = sizeof(Req);
559 Req.pSession = NIL_RTR0PTR;
560 Req.hIf = pThis->hIf;
561 Req.fPromiscuous = fPromiscuous;
562 int rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req, sizeof(Req));
563#else /* IN_RING0 */
564 int rc = IntNetR0IfSetPromiscuousMode(pThis->hIf, pThis->pSupDrvSession, fPromiscuous);
565#endif /* IN_RING0 */
566
567 LogFlow(("drvIntNetUp_SetPromiscuousMode: fPromiscuous=%RTbool\n", fPromiscuous));
568 AssertRC(rc);
569}
570
571#ifdef IN_RING3
572
573/**
574 * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
575 */
576static DECLCALLBACK(void) drvR3IntNetUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
577{
578 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
579 bool fLinkDown;
580 switch (enmLinkState)
581 {
582 case PDMNETWORKLINKSTATE_DOWN:
583 case PDMNETWORKLINKSTATE_DOWN_RESUME:
584 fLinkDown = true;
585 break;
586 default:
587 AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
588 RT_FALL_THRU();
589 case PDMNETWORKLINKSTATE_UP:
590 fLinkDown = false;
591 break;
592 }
593 LogFlow(("drvR3IntNetUp_NotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown));
594 ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
595}
596
597
598/* -=-=-=-=- Transmit Thread -=-=-=-=- */
599
600/**
601 * Async I/O thread for deferred packet transmission.
602 *
603 * @returns VBox status code. Returning failure will naturally terminate the thread.
604 * @param pDrvIns The internal networking driver instance.
605 * @param pThread The thread.
606 */
607static DECLCALLBACK(int) drvR3IntNetXmitThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
608{
609 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
610
611 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
612 {
613 /*
614 * Transmit any pending packets.
615 */
616 /** @todo Optimize this. We shouldn't call pfnXmitPending unless asked for.
617 * Also there is no need to call drvIntNetProcessXmit if we also
618 * called pfnXmitPending and send one or more frames. */
619 if (ASMAtomicXchgBool(&pThis->fXmitProcessRing, false))
620 {
621 STAM_REL_COUNTER_INC(&pThis->StatXmitProcessRing);
622 PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
623 drvIntNetProcessXmit(pThis);
624 PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
625 }
626
627 pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
628
629 if (ASMAtomicXchgBool(&pThis->fXmitProcessRing, false))
630 {
631 STAM_REL_COUNTER_INC(&pThis->StatXmitProcessRing);
632 PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
633 drvIntNetProcessXmit(pThis);
634 PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
635 }
636
637 /*
638 * Block until we've got something to send or is supposed
639 * to leave the running state.
640 */
641 int rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hXmitEvt, RT_INDEFINITE_WAIT);
642 AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
643 if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
644 break;
645
646 }
647
648 /* The thread is being initialized, suspended or terminated. */
649 return VINF_SUCCESS;
650}
651
652
653/**
654 * @copydoc FNPDMTHREADWAKEUPDRV
655 */
656static DECLCALLBACK(int) drvR3IntNetXmitWakeUp(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
657{
658 RT_NOREF(pThread);
659 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
660 return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
661}
662
663
664/* -=-=-=-=- Receive Thread -=-=-=-=- */
665
666/**
667 * Wait for space to become available up the driver/device chain.
668 *
669 * @returns VINF_SUCCESS if space is available.
670 * @returns VERR_STATE_CHANGED if the state changed.
671 * @returns VBox status code on other errors.
672 * @param pThis Pointer to the instance data.
673 */
674static int drvR3IntNetRecvWaitForSpace(PDRVINTNET pThis)
675{
676 LogFlow(("drvR3IntNetRecvWaitForSpace:\n"));
677 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
678 int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
679 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
680 LogFlow(("drvR3IntNetRecvWaitForSpace: returns %Rrc\n", rc));
681 return rc;
682}
683
684
685/**
686 * Executes async I/O (RUNNING mode).
687 *
688 * @returns VERR_STATE_CHANGED if the state changed.
689 * @returns Appropriate VBox status code (error) on fatal error.
690 * @param pThis The driver instance data.
691 */
692static int drvR3IntNetRecvRun(PDRVINTNET pThis)
693{
694 PPDMDRVINS pDrvIns = pThis->pDrvInsR3;
695 LogFlow(("drvR3IntNetRecvRun: pThis=%p\n", pThis));
696
697 /*
698 * The running loop - processing received data and waiting for more to arrive.
699 */
700 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
701 PINTNETBUF pBuf = pThis->CTX_SUFF(pBuf);
702 PINTNETRINGBUF pRingBuf = &pBuf->Recv;
703 for (;;)
704 {
705 /*
706 * Process the receive buffer.
707 */
708 PINTNETHDR pHdr;
709 while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)) != NULL)
710 {
711 /*
712 * Check the state and then inspect the packet.
713 */
714 if (pThis->enmRecvState != RECVSTATE_RUNNING)
715 {
716 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
717 LogFlow(("drvR3IntNetRecvRun: returns VERR_STATE_CHANGED (state changed - #0)\n"));
718 return VERR_STATE_CHANGED;
719 }
720
721 Log2(("pHdr=%p offRead=%#x: %.8Rhxs\n", pHdr, pRingBuf->offReadX, pHdr));
722 uint8_t u8Type = pHdr->u8Type;
723 if ( ( u8Type == INTNETHDR_TYPE_FRAME
724 || u8Type == INTNETHDR_TYPE_GSO)
725 && !pThis->fLinkDown)
726 {
727 /*
728 * Check if there is room for the frame and pass it up.
729 */
730 size_t cbFrame = pHdr->cbFrame;
731 int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, 0);
732 if (rc == VINF_SUCCESS)
733 {
734 if (u8Type == INTNETHDR_TYPE_FRAME)
735 {
736 /*
737 * Normal frame.
738 */
739#ifdef LOG_ENABLED
740 if (LogIsEnabled())
741 {
742 uint64_t u64Now = RTTimeProgramNanoTS();
743 LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
744 cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
745 pThis->u64LastReceiveTS = u64Now;
746 Log2(("drvR3IntNetRecvRun: cbFrame=%#x\n"
747 "%.*Rhxd\n",
748 cbFrame, cbFrame, IntNetHdrGetFramePtr(pHdr, pBuf)));
749 }
750#endif
751 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, IntNetHdrGetFramePtr(pHdr, pBuf), cbFrame);
752 AssertRC(rc);
753
754 /* skip to the next frame. */
755 IntNetRingSkipFrame(pRingBuf);
756 }
757 else
758 {
759 /*
760 * Generic segment offload frame (INTNETHDR_TYPE_GSO).
761 */
762 STAM_COUNTER_INC(&pThis->StatReceivedGso);
763 PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pBuf);
764 if (PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(PDMNETWORKGSO)))
765 {
766 if ( !pThis->pIAboveNet->pfnReceiveGso
767 || RT_FAILURE(pThis->pIAboveNet->pfnReceiveGso(pThis->pIAboveNet,
768 (uint8_t *)(pGso + 1),
769 pHdr->cbFrame - sizeof(PDMNETWORKGSO),
770 pGso)))
771 {
772 /*
773 * This is where we do the offloading since this NIC
774 * does not support large receive offload (LRO).
775 */
776 cbFrame -= sizeof(PDMNETWORKGSO);
777
778 uint8_t abHdrScratch[256];
779 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
780#ifdef LOG_ENABLED
781 if (LogIsEnabled())
782 {
783 uint64_t u64Now = RTTimeProgramNanoTS();
784 LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu; GSO - %u segs\n",
785 cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS, cSegs));
786 pThis->u64LastReceiveTS = u64Now;
787 Log2(("drvR3IntNetRecvRun: cbFrame=%#x type=%d cbHdrsTotal=%#x cbHdrsSeg=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n"
788 "%.*Rhxd\n",
789 cbFrame, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg,
790 cbFrame - sizeof(*pGso), pGso + 1));
791 }
792#endif
793 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
794 {
795 uint32_t cbSegFrame;
796 void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame,
797 abHdrScratch, iSeg, cSegs, &cbSegFrame);
798 rc = drvR3IntNetRecvWaitForSpace(pThis);
799 if (RT_FAILURE(rc))
800 {
801 Log(("drvR3IntNetRecvRun: drvR3IntNetRecvWaitForSpace -> %Rrc; iSeg=%u cSegs=%u\n", rc, iSeg, cSegs));
802 break; /* we drop the rest. */
803 }
804 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvSegFrame, cbSegFrame);
805 AssertRC(rc);
806 }
807 }
808 }
809 else
810 {
811 AssertMsgFailed(("cbFrame=%#x type=%d cbHdrsTotal=%#x cbHdrsSeg=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n",
812 cbFrame, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg));
813 STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
814 }
815
816 IntNetRingSkipFrame(pRingBuf);
817 }
818 }
819 else
820 {
821 /*
822 * Wait for sufficient space to become available and then retry.
823 */
824 rc = drvR3IntNetRecvWaitForSpace(pThis);
825 if (RT_FAILURE(rc))
826 {
827 if (rc == VERR_INTERRUPTED)
828 {
829 /*
830 * NIC is going down, likely because the VM is being reset. Skip the frame.
831 */
832 AssertMsg(IntNetIsValidFrameType(pHdr->u8Type), ("Unknown frame type %RX16! offRead=%#x\n", pHdr->u8Type, pRingBuf->offReadX));
833 IntNetRingSkipFrame(pRingBuf);
834 }
835 else
836 {
837 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
838 LogFlow(("drvR3IntNetRecvRun: returns %Rrc (wait-for-space)\n", rc));
839 return rc;
840 }
841 }
842 }
843 }
844 else
845 {
846 /*
847 * Link down or unknown frame - skip to the next frame.
848 */
849 AssertMsg(IntNetIsValidFrameType(pHdr->u8Type), ("Unknown frame type %RX16! offRead=%#x\n", pHdr->u8Type, pRingBuf->offReadX));
850 IntNetRingSkipFrame(pRingBuf);
851 STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
852 }
853 } /* while more received data */
854
855 /*
856 * Wait for data, checking the state before we block.
857 */
858 if (pThis->enmRecvState != RECVSTATE_RUNNING)
859 {
860 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
861 LogFlow(("drvR3IntNetRecvRun: returns VINF_SUCCESS (state changed - #1)\n"));
862 return VERR_STATE_CHANGED;
863 }
864 INTNETIFWAITREQ WaitReq;
865 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
866 WaitReq.Hdr.cbReq = sizeof(WaitReq);
867 WaitReq.pSession = NIL_RTR0PTR;
868 WaitReq.hIf = pThis->hIf;
869 WaitReq.cMillies = 30000; /* 30s - don't wait forever, timeout now and then. */
870 STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
871 int rc = PDMDrvHlpSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_WAIT, &WaitReq, sizeof(WaitReq));
872 if ( RT_FAILURE(rc)
873 && rc != VERR_TIMEOUT
874 && rc != VERR_INTERRUPTED)
875 {
876 LogFlow(("drvR3IntNetRecvRun: returns %Rrc\n", rc));
877 return rc;
878 }
879 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
880 }
881}
882
883
884/**
885 * Asynchronous I/O thread for handling receive.
886 *
887 * @returns VINF_SUCCESS (ignored).
888 * @param hThreadSelf Thread handle.
889 * @param pvUser Pointer to a DRVINTNET structure.
890 */
891static DECLCALLBACK(int) drvR3IntNetRecvThread(RTTHREAD hThreadSelf, void *pvUser)
892{
893 RT_NOREF(hThreadSelf);
894 PDRVINTNET pThis = (PDRVINTNET)pvUser;
895 LogFlow(("drvR3IntNetRecvThread: pThis=%p\n", pThis));
896 STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
897
898 /*
899 * The main loop - acting on state.
900 */
901 for (;;)
902 {
903 RECVSTATE enmRecvState = pThis->enmRecvState;
904 switch (enmRecvState)
905 {
906 case RECVSTATE_SUSPENDED:
907 {
908 int rc = RTSemEventWait(pThis->hRecvEvt, 30000);
909 if ( RT_FAILURE(rc)
910 && rc != VERR_TIMEOUT)
911 {
912 LogFlow(("drvR3IntNetRecvThread: returns %Rrc\n", rc));
913 return rc;
914 }
915 break;
916 }
917
918 case RECVSTATE_RUNNING:
919 {
920 int rc = drvR3IntNetRecvRun(pThis);
921 if ( rc != VERR_STATE_CHANGED
922 && RT_FAILURE(rc))
923 {
924 LogFlow(("drvR3IntNetRecvThread: returns %Rrc\n", rc));
925 return rc;
926 }
927 break;
928 }
929
930 default:
931 AssertMsgFailed(("Invalid state %d\n", enmRecvState));
932 RT_FALL_THRU();
933 case RECVSTATE_TERMINATE:
934 LogFlow(("drvR3IntNetRecvThread: returns VINF_SUCCESS\n"));
935 return VINF_SUCCESS;
936 }
937 }
938}
939
940
941#ifdef VBOX_WITH_DRVINTNET_IN_R0
942
943/* -=-=-=-=- PDMIBASERC -=-=-=-=- */
944
945/**
946 * @interface_method_impl{PDMIBASERC,pfnQueryInterface}
947 */
948static DECLCALLBACK(RTRCPTR) drvR3IntNetIBaseRC_QueryInterface(PPDMIBASERC pInterface, const char *pszIID)
949{
950 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, IBaseRC);
951#if 0
952 PDMIBASERC_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpRC);
953#else
954 RT_NOREF(pThis, pszIID);
955#endif
956 return NIL_RTRCPTR;
957}
958
959
960/* -=-=-=-=- PDMIBASER0 -=-=-=-=- */
961
962/**
963 * @interface_method_impl{PDMIBASER0,pfnQueryInterface}
964 */
965static DECLCALLBACK(RTR0PTR) drvR3IntNetIBaseR0_QueryInterface(PPDMIBASER0 pInterface, const char *pszIID)
966{
967 PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, IBaseR0);
968 PDMIBASER0_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpR0);
969 return NIL_RTR0PTR;
970}
971
972#endif /* VBOX_WITH_DRVINTNET_IN_R0 */
973
974/* -=-=-=-=- PDMIBASE -=-=-=-=- */
975
976
977/**
978 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
979 */
980static DECLCALLBACK(void *) drvR3IntNetIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
981{
982 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
983 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
984
985 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
986#ifdef VBOX_WITH_DRVINTNET_IN_R0
987 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASER0, &pThis->IBaseR0);
988 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASERC, &pThis->IBaseRC);
989#endif
990 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUpR3);
991 return NULL;
992}
993
994
995/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
996
997/**
998 * Power Off notification.
999 *
1000 * @param pDrvIns The driver instance.
1001 */
1002static DECLCALLBACK(void) drvR3IntNetPowerOff(PPDMDRVINS pDrvIns)
1003{
1004 LogFlow(("drvR3IntNetPowerOff\n"));
1005 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1006 if (!pThis->fActivateEarlyDeactivateLate)
1007 {
1008 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_SUSPENDED);
1009 drvR3IntNetSetActive(pThis, false /* fActive */);
1010 }
1011}
1012
1013
1014/**
1015 * drvR3IntNetResume helper.
1016 */
1017static int drvR3IntNetResumeSend(PDRVINTNET pThis, const void *pvBuf, size_t cb)
1018{
1019 /*
1020 * Add the frame to the send buffer and push it onto the network.
1021 */
1022 int rc = IntNetRingWriteFrame(&pThis->pBufR3->Send, pvBuf, (uint32_t)cb);
1023 if ( rc == VERR_BUFFER_OVERFLOW
1024 && pThis->pBufR3->cbSend < cb)
1025 {
1026 INTNETIFSENDREQ SendReq;
1027 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1028 SendReq.Hdr.cbReq = sizeof(SendReq);
1029 SendReq.pSession = NIL_RTR0PTR;
1030 SendReq.hIf = pThis->hIf;
1031 PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
1032
1033 rc = IntNetRingWriteFrame(&pThis->pBufR3->Send, pvBuf, (uint32_t)cb);
1034 }
1035
1036 if (RT_SUCCESS(rc))
1037 {
1038 INTNETIFSENDREQ SendReq;
1039 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1040 SendReq.Hdr.cbReq = sizeof(SendReq);
1041 SendReq.pSession = NIL_RTR0PTR;
1042 SendReq.hIf = pThis->hIf;
1043 rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
1044 }
1045
1046 AssertRC(rc);
1047 return rc;
1048}
1049
1050
1051/**
1052 * Resume notification.
1053 *
1054 * @param pDrvIns The driver instance.
1055 */
1056static DECLCALLBACK(void) drvR3IntNetResume(PPDMDRVINS pDrvIns)
1057{
1058 LogFlow(("drvR3IntNetPowerResume\n"));
1059 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1060 VMRESUMEREASON enmReason = PDMDrvHlpVMGetResumeReason(pDrvIns);
1061
1062 if (!pThis->fActivateEarlyDeactivateLate)
1063 {
1064 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
1065 RTSemEventSignal(pThis->hRecvEvt);
1066 drvR3IntNetUpdateMacAddress(pThis); /* (could be a state restore) */
1067 drvR3IntNetSetActive(pThis, true /* fActive */);
1068 }
1069
1070 switch (enmReason)
1071 {
1072 case VMRESUMEREASON_HOST_RESUME:
1073 {
1074 uint32_t u32TrunkType;
1075 int rc = pDrvIns->pHlpR3->pfnCFGMQueryU32(pDrvIns->pCfg, "TrunkType", &u32TrunkType);
1076 AssertRC(rc);
1077
1078 /*
1079 * Only do the disconnect for bridged networking. Host-only and
1080 * internal networks are not affected by a host resume.
1081 */
1082 if ( RT_SUCCESS(rc)
1083 && u32TrunkType == kIntNetTrunkType_NetFlt)
1084 {
1085 rc = pThis->pIAboveConfigR3->pfnSetLinkState(pThis->pIAboveConfigR3,
1086 PDMNETWORKLINKSTATE_DOWN_RESUME);
1087 AssertRC(rc);
1088 }
1089 break;
1090 }
1091 case VMRESUMEREASON_TELEPORTED:
1092 case VMRESUMEREASON_TELEPORT_FAILED:
1093 {
1094 if ( PDMDrvHlpVMTeleportedAndNotFullyResumedYet(pDrvIns)
1095 && pThis->pIAboveConfigR3)
1096 {
1097 /*
1098 * We've just been teleported and need to drop a hint to the switch
1099 * since we're likely to have changed to a different port. We just
1100 * push out some ethernet frame that doesn't mean anything to anyone.
1101 * For this purpose ethertype 0x801e was chosen since it was registered
1102 * to Sun (dunno what it is/was used for though).
1103 */
1104 union
1105 {
1106 RTNETETHERHDR Hdr;
1107 uint8_t ab[128];
1108 } Frame;
1109 RT_ZERO(Frame);
1110 Frame.Hdr.DstMac.au16[0] = 0xffff;
1111 Frame.Hdr.DstMac.au16[1] = 0xffff;
1112 Frame.Hdr.DstMac.au16[2] = 0xffff;
1113 Frame.Hdr.EtherType = RT_H2BE_U16_C(0x801e);
1114 int rc = pThis->pIAboveConfigR3->pfnGetMac(pThis->pIAboveConfigR3,
1115 &Frame.Hdr.SrcMac);
1116 if (RT_SUCCESS(rc))
1117 rc = drvR3IntNetResumeSend(pThis, &Frame, sizeof(Frame));
1118 if (RT_FAILURE(rc))
1119 LogRel(("IntNet#%u: Sending dummy frame failed: %Rrc\n",
1120 pDrvIns->iInstance, rc));
1121 }
1122 break;
1123 }
1124 default: /* ignore every other resume reason else */
1125 break;
1126 } /* end of switch(enmReason) */
1127}
1128
1129
1130/**
1131 * Suspend notification.
1132 *
1133 * @param pDrvIns The driver instance.
1134 */
1135static DECLCALLBACK(void) drvR3IntNetSuspend(PPDMDRVINS pDrvIns)
1136{
1137 LogFlow(("drvR3IntNetPowerSuspend\n"));
1138 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1139 if (!pThis->fActivateEarlyDeactivateLate)
1140 {
1141 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_SUSPENDED);
1142 drvR3IntNetSetActive(pThis, false /* fActive */);
1143 }
1144}
1145
1146
1147/**
1148 * Power On notification.
1149 *
1150 * @param pDrvIns The driver instance.
1151 */
1152static DECLCALLBACK(void) drvR3IntNetPowerOn(PPDMDRVINS pDrvIns)
1153{
1154 LogFlow(("drvR3IntNetPowerOn\n"));
1155 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1156 if (!pThis->fActivateEarlyDeactivateLate)
1157 {
1158 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
1159 RTSemEventSignal(pThis->hRecvEvt);
1160 drvR3IntNetUpdateMacAddress(pThis);
1161 drvR3IntNetSetActive(pThis, true /* fActive */);
1162 }
1163}
1164
1165
1166/**
1167 * @interface_method_impl{PDMDRVREG,pfnRelocate}
1168 */
1169static DECLCALLBACK(void) drvR3IntNetRelocate(PPDMDRVINS pDrvIns, RTGCINTPTR offDelta)
1170{
1171 /* nothing to do here yet */
1172 RT_NOREF(pDrvIns, offDelta);
1173}
1174
1175
1176/**
1177 * Destruct a driver instance.
1178 *
1179 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1180 * resources can be freed correctly.
1181 *
1182 * @param pDrvIns The driver instance data.
1183 */
1184static DECLCALLBACK(void) drvR3IntNetDestruct(PPDMDRVINS pDrvIns)
1185{
1186 LogFlow(("drvR3IntNetDestruct\n"));
1187 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1188 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1189
1190 /*
1191 * Indicate to the receive thread that it's time to quit.
1192 */
1193 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_TERMINATE);
1194 ASMAtomicXchgSize(&pThis->fLinkDown, true);
1195 RTSEMEVENT hRecvEvt = pThis->hRecvEvt;
1196 pThis->hRecvEvt = NIL_RTSEMEVENT;
1197
1198 if (hRecvEvt != NIL_RTSEMEVENT)
1199 RTSemEventSignal(hRecvEvt);
1200
1201 if (pThis->hIf != INTNET_HANDLE_INVALID)
1202 {
1203 INTNETIFABORTWAITREQ AbortWaitReq;
1204 AbortWaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1205 AbortWaitReq.Hdr.cbReq = sizeof(AbortWaitReq);
1206 AbortWaitReq.pSession = NIL_RTR0PTR;
1207 AbortWaitReq.hIf = pThis->hIf;
1208 AbortWaitReq.fNoMoreWaits = true;
1209 int rc = PDMDrvHlpSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_ABORT_WAIT, &AbortWaitReq, sizeof(AbortWaitReq));
1210 AssertMsg(RT_SUCCESS(rc) || rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc)); RT_NOREF_PV(rc);
1211 }
1212
1213 /*
1214 * Wait for the threads to terminate.
1215 */
1216 if (pThis->pXmitThread)
1217 {
1218 int rc = PDMDrvHlpThreadDestroy(pDrvIns, pThis->pXmitThread, NULL);
1219 AssertRC(rc);
1220 pThis->pXmitThread = NULL;
1221 }
1222
1223 if (pThis->hRecvThread != NIL_RTTHREAD)
1224 {
1225 int rc = RTThreadWait(pThis->hRecvThread, 5000, NULL);
1226 AssertRC(rc);
1227 pThis->hRecvThread = NIL_RTTHREAD;
1228 }
1229
1230 /*
1231 * Deregister statistics in case we're being detached.
1232 */
1233 if (pThis->pBufR3)
1234 {
1235 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cStatFrames);
1236 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cbStatWritten);
1237 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cOverflows);
1238 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cStatFrames);
1239 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cbStatWritten);
1240 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cOverflows);
1241 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatYieldsOk);
1242 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatYieldsNok);
1243 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatLost);
1244 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatBadFrames);
1245 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatSend1);
1246 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatSend2);
1247 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatRecv1);
1248 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatRecv2);
1249 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatReserved);
1250 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceivedGso);
1251 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatSentGso);
1252#ifdef VBOX_WITH_STATISTICS
1253 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
1254 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
1255#endif
1256 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitWakeupR0);
1257 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitWakeupR3);
1258 PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitProcessRing);
1259 }
1260
1261 /*
1262 * Close the interface
1263 */
1264 if (pThis->hIf != INTNET_HANDLE_INVALID)
1265 {
1266 INTNETIFCLOSEREQ CloseReq;
1267 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1268 CloseReq.Hdr.cbReq = sizeof(CloseReq);
1269 CloseReq.pSession = NIL_RTR0PTR;
1270 CloseReq.hIf = pThis->hIf;
1271 pThis->hIf = INTNET_HANDLE_INVALID;
1272 int rc = PDMDrvHlpSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq, sizeof(CloseReq));
1273 AssertRC(rc);
1274 }
1275
1276
1277 /*
1278 * Destroy the semaphores, S/G cache and xmit lock.
1279 */
1280 if (hRecvEvt != NIL_RTSEMEVENT)
1281 RTSemEventDestroy(hRecvEvt);
1282
1283 if (pThis->hXmitEvt != NIL_SUPSEMEVENT)
1284 {
1285 SUPSemEventClose(pThis->pSupDrvSession, pThis->hXmitEvt);
1286 pThis->hXmitEvt = NIL_SUPSEMEVENT;
1287 }
1288
1289 RTMemCacheDestroy(pThis->hSgCache);
1290 pThis->hSgCache = NIL_RTMEMCACHE;
1291
1292 if (PDMDrvHlpCritSectIsInitialized(pDrvIns, &pThis->XmitLock))
1293 PDMDrvHlpCritSectDelete(pDrvIns, &pThis->XmitLock);
1294}
1295
1296
1297/**
1298 * Queries a policy config value and translates it into open network flag.
1299 *
1300 * @returns VBox status code (error set on failure).
1301 * @param pDrvIns The driver instance.
1302 * @param pszName The value name.
1303 * @param paFlags The open network flag descriptors.
1304 * @param cFlags The number of descriptors.
1305 * @param fFlags The fixed flag.
1306 * @param pfFlags The flags variable to update.
1307 */
1308static int drvIntNetR3CfgGetPolicy(PPDMDRVINS pDrvIns, const char *pszName, PCDRVINTNETFLAG paFlags, size_t cFlags,
1309 uint32_t fFixedFlag, uint32_t *pfFlags)
1310{
1311 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1312
1313 char szValue[64];
1314 int rc = pHlp->pfnCFGMQueryString(pDrvIns->pCfg, pszName, szValue, sizeof(szValue));
1315 if (RT_FAILURE(rc))
1316 {
1317 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1318 return VINF_SUCCESS;
1319 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1320 N_("Configuration error: Failed to query value of \"%s\""), pszName);
1321 }
1322
1323 /*
1324 * Check for +fixed first, so it can be stripped off.
1325 */
1326 char *pszSep = strpbrk(szValue, "+,;");
1327 if (pszSep)
1328 {
1329 *pszSep++ = '\0';
1330 const char *pszFixed = RTStrStripL(pszSep);
1331 if (strcmp(pszFixed, "fixed"))
1332 {
1333 *pszSep = '+';
1334 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1335 N_("Configuration error: The value of \"%s\" is unknown: \"%s\""), pszName, szValue);
1336 }
1337 *pfFlags |= fFixedFlag;
1338 RTStrStripR(szValue);
1339 }
1340
1341 /*
1342 * Match against the flag values.
1343 */
1344 size_t i = cFlags;
1345 while (i-- > 0)
1346 if (!strcmp(paFlags[i].pszChoice, szValue))
1347 {
1348 *pfFlags |= paFlags[i].fFlag;
1349 return VINF_SUCCESS;
1350 }
1351
1352 if (!strcmp(szValue, "none"))
1353 return VINF_SUCCESS;
1354
1355 if (!strcmp(szValue, "fixed"))
1356 {
1357 *pfFlags |= fFixedFlag;
1358 return VINF_SUCCESS;
1359 }
1360
1361 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
1362 N_("Configuration error: The value of \"%s\" is unknown: \"%s\""), pszName, szValue);
1363}
1364
1365
1366/**
1367 * Construct a TAP network transport driver instance.
1368 *
1369 * @copydoc FNPDMDRVCONSTRUCT
1370 */
1371static DECLCALLBACK(int) drvR3IntNetConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1372{
1373 RT_NOREF(fFlags);
1374 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1375 PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
1376 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
1377 bool f;
1378
1379 /*
1380 * Init the static parts.
1381 */
1382 pThis->pDrvInsR3 = pDrvIns;
1383#ifdef VBOX_WITH_DRVINTNET_IN_R0
1384 pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
1385#endif
1386 pThis->hIf = INTNET_HANDLE_INVALID;
1387 pThis->hRecvThread = NIL_RTTHREAD;
1388 pThis->hRecvEvt = NIL_RTSEMEVENT;
1389 pThis->pXmitThread = NULL;
1390 pThis->hXmitEvt = NIL_SUPSEMEVENT;
1391 pThis->pSupDrvSession = PDMDrvHlpGetSupDrvSession(pDrvIns);
1392 pThis->hSgCache = NIL_RTMEMCACHE;
1393 pThis->enmRecvState = RECVSTATE_SUSPENDED;
1394 pThis->fActivateEarlyDeactivateLate = false;
1395 /* IBase* */
1396 pDrvIns->IBase.pfnQueryInterface = drvR3IntNetIBase_QueryInterface;
1397#ifdef VBOX_WITH_DRVINTNET_IN_R0
1398 pThis->IBaseR0.pfnQueryInterface = drvR3IntNetIBaseR0_QueryInterface;
1399 pThis->IBaseRC.pfnQueryInterface = drvR3IntNetIBaseRC_QueryInterface;
1400#endif
1401 /* INetworkUp */
1402 pThis->INetworkUpR3.pfnBeginXmit = drvIntNetUp_BeginXmit;
1403 pThis->INetworkUpR3.pfnAllocBuf = drvIntNetUp_AllocBuf;
1404 pThis->INetworkUpR3.pfnFreeBuf = drvIntNetUp_FreeBuf;
1405 pThis->INetworkUpR3.pfnSendBuf = drvIntNetUp_SendBuf;
1406 pThis->INetworkUpR3.pfnEndXmit = drvIntNetUp_EndXmit;
1407 pThis->INetworkUpR3.pfnSetPromiscuousMode = drvIntNetUp_SetPromiscuousMode;
1408 pThis->INetworkUpR3.pfnNotifyLinkChanged = drvR3IntNetUp_NotifyLinkChanged;
1409
1410 /*
1411 * Validate the config.
1412 */
1413 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
1414 "Network"
1415 "|Trunk"
1416 "|TrunkType"
1417 "|ReceiveBufferSize"
1418 "|SendBufferSize"
1419 "|SharedMacOnWire"
1420 "|RestrictAccess"
1421 "|RequireExactPolicyMatch"
1422 "|RequireAsRestrictivePolicy"
1423 "|AccessPolicy"
1424 "|PromiscPolicyClients"
1425 "|PromiscPolicyHost"
1426 "|PromiscPolicyWire"
1427 "|IfPolicyPromisc"
1428 "|TrunkPolicyHost"
1429 "|TrunkPolicyWire"
1430 "|IsService"
1431 "|IgnoreConnectFailure"
1432 "|Workaround1",
1433 "");
1434
1435 /*
1436 * Check that no-one is attached to us.
1437 */
1438 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
1439 ("Configuration error: Not possible to attach anything to this driver!\n"),
1440 VERR_PDM_DRVINS_NO_ATTACH);
1441
1442 /*
1443 * Query the network port interface.
1444 */
1445 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1446 if (!pThis->pIAboveNet)
1447 {
1448 AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
1449 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1450 }
1451 pThis->pIAboveConfigR3 = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1452
1453 /*
1454 * Read the configuration.
1455 */
1456 INTNETOPENREQ OpenReq;
1457 RT_ZERO(OpenReq);
1458 OpenReq.Hdr.cbReq = sizeof(OpenReq);
1459 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1460 OpenReq.pSession = NIL_RTR0PTR;
1461
1462 /** @cfgm{Network, string}
1463 * The name of the internal network to connect to.
1464 */
1465 int rc = pHlp->pfnCFGMQueryString(pCfg, "Network", OpenReq.szNetwork, sizeof(OpenReq.szNetwork));
1466 if (RT_FAILURE(rc))
1467 return PDMDRV_SET_ERROR(pDrvIns, rc,
1468 N_("Configuration error: Failed to get the \"Network\" value"));
1469 strcpy(pThis->szNetwork, OpenReq.szNetwork);
1470
1471 /** @cfgm{TrunkType, uint32_t, kIntNetTrunkType_None}
1472 * The trunk connection type see INTNETTRUNKTYPE.
1473 */
1474 uint32_t u32TrunkType;
1475 rc = pHlp->pfnCFGMQueryU32(pCfg, "TrunkType", &u32TrunkType);
1476 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1477 u32TrunkType = kIntNetTrunkType_None;
1478 else if (RT_FAILURE(rc))
1479 return PDMDRV_SET_ERROR(pDrvIns, rc,
1480 N_("Configuration error: Failed to get the \"TrunkType\" value"));
1481 OpenReq.enmTrunkType = (INTNETTRUNKTYPE)u32TrunkType;
1482
1483 /** @cfgm{Trunk, string, ""}
1484 * The name of the trunk connection.
1485 */
1486 rc = pHlp->pfnCFGMQueryString(pCfg, "Trunk", OpenReq.szTrunk, sizeof(OpenReq.szTrunk));
1487 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1488 OpenReq.szTrunk[0] = '\0';
1489 else if (RT_FAILURE(rc))
1490 return PDMDRV_SET_ERROR(pDrvIns, rc,
1491 N_("Configuration error: Failed to get the \"Trunk\" value"));
1492
1493 OpenReq.fFlags = 0;
1494
1495 /** @cfgm{SharedMacOnWire, boolean, false}
1496 * Whether to shared the MAC address of the host interface when using the wire. When
1497 * attaching to a wireless NIC this option is usually a requirement.
1498 */
1499 bool fSharedMacOnWire;
1500 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SharedMacOnWire", &fSharedMacOnWire, false);
1501 if (RT_FAILURE(rc))
1502 return PDMDRV_SET_ERROR(pDrvIns, rc,
1503 N_("Configuration error: Failed to get the \"SharedMacOnWire\" value"));
1504 if (fSharedMacOnWire)
1505 OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
1506
1507 /** @cfgm{RestrictAccess, boolean, true}
1508 * Whether to restrict the access to the network or if it should be public.
1509 * Everyone on the computer can connect to a public network.
1510 * @deprecated Use AccessPolicy instead.
1511 */
1512 rc = pHlp->pfnCFGMQueryBool(pCfg, "RestrictAccess", &f);
1513 if (RT_SUCCESS(rc))
1514 {
1515 if (f)
1516 OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_RESTRICTED;
1517 else
1518 OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_PUBLIC;
1519 OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_FIXED;
1520 }
1521 else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
1522 return PDMDRV_SET_ERROR(pDrvIns, rc,
1523 N_("Configuration error: Failed to get the \"RestrictAccess\" value"));
1524
1525 /** @cfgm{RequireExactPolicyMatch, boolean, false}
1526 * Whether to require that the current security and promiscuous policies of
1527 * the network is exactly as the ones specified in this open network
1528 * request. Use this with RequireAsRestrictivePolicy to prevent
1529 * restrictions from being lifted. If no further policy changes are
1530 * desired, apply the relevant fixed flags. */
1531 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RequireExactPolicyMatch", &f, false);
1532 if (RT_FAILURE(rc))
1533 return PDMDRV_SET_ERROR(pDrvIns, rc,
1534 N_("Configuration error: Failed to get the \"RequireExactPolicyMatch\" value"));
1535 if (f)
1536 OpenReq.fFlags |= INTNET_OPEN_FLAGS_REQUIRE_EXACT;
1537
1538 /** @cfgm{RequireAsRestrictivePolicy, boolean, false}
1539 * Whether to require that the security and promiscuous policies of the
1540 * network is at least as restrictive as specified this request specifies
1541 * and prevent them being lifted later on.
1542 */
1543 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RequireAsRestrictivePolicy", &f, false);
1544 if (RT_FAILURE(rc))
1545 return PDMDRV_SET_ERROR(pDrvIns, rc,
1546 N_("Configuration error: Failed to get the \"RequireAsRestrictivePolicy\" value"));
1547 if (f)
1548 OpenReq.fFlags |= INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES;
1549
1550 /** @cfgm{AccessPolicy, string, "none"}
1551 * The access policy of the network:
1552 * public, public+fixed, restricted, restricted+fixed, none or fixed.
1553 *
1554 * A "public" network is accessible to everyone on the same host, while a
1555 * "restricted" one is only accessible to VMs & services started by the
1556 * same user. The "none" policy, which is the default, means no policy
1557 * change or choice is made and that the current (existing network) or
1558 * default (new) policy should be used. */
1559 static const DRVINTNETFLAG s_aAccessPolicyFlags[] =
1560 {
1561 { "public", INTNET_OPEN_FLAGS_ACCESS_PUBLIC },
1562 { "restricted", INTNET_OPEN_FLAGS_ACCESS_RESTRICTED }
1563 };
1564 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "AccessPolicy", &s_aAccessPolicyFlags[0], RT_ELEMENTS(s_aAccessPolicyFlags),
1565 INTNET_OPEN_FLAGS_ACCESS_FIXED, &OpenReq.fFlags);
1566 AssertRCReturn(rc, rc);
1567
1568 /** @cfgm{PromiscPolicyClients, string, "none"}
1569 * The network wide promiscuous mode policy for client (non-trunk)
1570 * interfaces: allow, allow+fixed, deny, deny+fixed, none or fixed. */
1571 static const DRVINTNETFLAG s_aPromiscPolicyClient[] =
1572 {
1573 { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS },
1574 { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS }
1575 };
1576 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyClients", &s_aPromiscPolicyClient[0], RT_ELEMENTS(s_aPromiscPolicyClient),
1577 INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
1578 AssertRCReturn(rc, rc);
1579 /** @cfgm{PromiscPolicyHost, string, "none"}
1580 * The promiscuous mode policy for the trunk-host
1581 * connection: allow, allow+fixed, deny, deny+fixed, none or fixed. */
1582 static const DRVINTNETFLAG s_aPromiscPolicyHost[] =
1583 {
1584 { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST },
1585 { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST }
1586 };
1587 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyHost", &s_aPromiscPolicyHost[0], RT_ELEMENTS(s_aPromiscPolicyHost),
1588 INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
1589 AssertRCReturn(rc, rc);
1590 /** @cfgm{PromiscPolicyWire, string, "none"}
1591 * The promiscuous mode policy for the trunk-host
1592 * connection: allow, allow+fixed, deny, deny+fixed, none or fixed. */
1593 static const DRVINTNETFLAG s_aPromiscPolicyWire[] =
1594 {
1595 { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE },
1596 { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE }
1597 };
1598 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyWire", &s_aPromiscPolicyWire[0], RT_ELEMENTS(s_aPromiscPolicyWire),
1599 INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
1600 AssertRCReturn(rc, rc);
1601
1602
1603 /** @cfgm{IfPolicyPromisc, string, "none"}
1604 * The promiscuous mode policy for this
1605 * interface: deny, deny+fixed, allow-all, allow-all+fixed, allow-network,
1606 * allow-network+fixed, none or fixed. */
1607 static const DRVINTNETFLAG s_aIfPolicyPromisc[] =
1608 {
1609 { "allow-all", INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK },
1610 { "allow-network", INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW | INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK },
1611 { "deny", INTNET_OPEN_FLAGS_IF_PROMISC_DENY }
1612 };
1613 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "IfPolicyPromisc", &s_aIfPolicyPromisc[0], RT_ELEMENTS(s_aIfPolicyPromisc),
1614 INTNET_OPEN_FLAGS_IF_FIXED, &OpenReq.fFlags);
1615 AssertRCReturn(rc, rc);
1616
1617
1618 /** @cfgm{TrunkPolicyHost, string, "none"}
1619 * The trunk-host policy: promisc, promisc+fixed, enabled, enabled+fixed,
1620 * disabled, disabled+fixed, none or fixed
1621 *
1622 * This can be used to prevent packages to be routed to the host. */
1623 static const DRVINTNETFLAG s_aTrunkPolicyHost[] =
1624 {
1625 { "promisc", INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED | INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE },
1626 { "enabled", INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED },
1627 { "disabled", INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED }
1628 };
1629 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "TrunkPolicyHost", &s_aTrunkPolicyHost[0], RT_ELEMENTS(s_aTrunkPolicyHost),
1630 INTNET_OPEN_FLAGS_TRUNK_FIXED, &OpenReq.fFlags);
1631 AssertRCReturn(rc, rc);
1632 /** @cfgm{TrunkPolicyWire, string, "none"}
1633 * The trunk-host policy: promisc, promisc+fixed, enabled, enabled+fixed,
1634 * disabled, disabled+fixed, none or fixed.
1635 *
1636 * This can be used to prevent packages to be routed to the wire. */
1637 static const DRVINTNETFLAG s_aTrunkPolicyWire[] =
1638 {
1639 { "promisc", INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED | INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE },
1640 { "enabled", INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED },
1641 { "disabled", INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED }
1642 };
1643 rc = drvIntNetR3CfgGetPolicy(pDrvIns, "TrunkPolicyWire", &s_aTrunkPolicyWire[0], RT_ELEMENTS(s_aTrunkPolicyWire),
1644 INTNET_OPEN_FLAGS_TRUNK_FIXED, &OpenReq.fFlags);
1645 AssertRCReturn(rc, rc);
1646
1647
1648 /** @cfgm{ReceiveBufferSize, uint32_t, 318 KB}
1649 * The size of the receive buffer.
1650 */
1651 rc = pHlp->pfnCFGMQueryU32(pCfg, "ReceiveBufferSize", &OpenReq.cbRecv);
1652 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1653 OpenReq.cbRecv = 318 * _1K ;
1654 else if (RT_FAILURE(rc))
1655 return PDMDRV_SET_ERROR(pDrvIns, rc,
1656 N_("Configuration error: Failed to get the \"ReceiveBufferSize\" value"));
1657
1658 /** @cfgm{SendBufferSize, uint32_t, 196 KB}
1659 * The size of the send (transmit) buffer.
1660 * This should be more than twice the size of the larges frame size because
1661 * the ring buffer is very simple and doesn't support splitting up frames
1662 * nor inserting padding. So, if this is too close to the frame size the
1663 * header will fragment the buffer such that the frame won't fit on either
1664 * side of it and the code will get very upset about it all.
1665 */
1666 rc = pHlp->pfnCFGMQueryU32(pCfg, "SendBufferSize", &OpenReq.cbSend);
1667 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1668 OpenReq.cbSend = RT_ALIGN_Z(VBOX_MAX_GSO_SIZE * 3, _1K);
1669 else if (RT_FAILURE(rc))
1670 return PDMDRV_SET_ERROR(pDrvIns, rc,
1671 N_("Configuration error: Failed to get the \"SendBufferSize\" value"));
1672 if (OpenReq.cbSend < 128)
1673 return PDMDRV_SET_ERROR(pDrvIns, rc,
1674 N_("Configuration error: The \"SendBufferSize\" value is too small"));
1675 if (OpenReq.cbSend < VBOX_MAX_GSO_SIZE * 3)
1676 LogRel(("DrvIntNet: Warning! SendBufferSize=%u, Recommended minimum size %u butes.\n", OpenReq.cbSend, VBOX_MAX_GSO_SIZE * 4));
1677
1678 /** @cfgm{IsService, boolean, true}
1679 * This alterns the way the thread is suspended and resumed. When it's being used by
1680 * a service such as LWIP/iSCSI it shouldn't suspend immediately like for a NIC.
1681 */
1682 rc = pHlp->pfnCFGMQueryBool(pCfg, "IsService", &pThis->fActivateEarlyDeactivateLate);
1683 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1684 pThis->fActivateEarlyDeactivateLate = false;
1685 else if (RT_FAILURE(rc))
1686 return PDMDRV_SET_ERROR(pDrvIns, rc,
1687 N_("Configuration error: Failed to get the \"IsService\" value"));
1688
1689
1690 /** @cfgm{IgnoreConnectFailure, boolean, false}
1691 * When set only raise a runtime error if we cannot connect to the internal
1692 * network. */
1693 bool fIgnoreConnectFailure;
1694 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreConnectFailure", &fIgnoreConnectFailure, false);
1695 if (RT_FAILURE(rc))
1696 return PDMDRV_SET_ERROR(pDrvIns, rc,
1697 N_("Configuration error: Failed to get the \"IgnoreConnectFailure\" value"));
1698
1699 /** @cfgm{Workaround1, boolean, depends}
1700 * Enables host specific workarounds, the default is depends on the whether
1701 * we think the host requires it or not.
1702 */
1703 bool fWorkaround1 = false;
1704#ifdef RT_OS_DARWIN
1705 if (OpenReq.fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1706 {
1707 char szKrnlVer[256];
1708 RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szKrnlVer, sizeof(szKrnlVer));
1709 if (strcmp(szKrnlVer, "10.7.0") >= 0)
1710 {
1711 LogRel(("IntNet#%u: Enables the workaround (ip_tos=0) for the little endian ip header checksum problem\n"));
1712 fWorkaround1 = true;
1713 }
1714 }
1715#endif
1716 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Workaround1", &fWorkaround1, fWorkaround1);
1717 if (RT_FAILURE(rc))
1718 return PDMDRV_SET_ERROR(pDrvIns, rc,
1719 N_("Configuration error: Failed to get the \"Workaround1\" value"));
1720 if (fWorkaround1)
1721 OpenReq.fFlags |= INTNET_OPEN_FLAGS_WORKAROUND_1;
1722
1723 LogRel(("IntNet#%u: szNetwork={%s} enmTrunkType=%d szTrunk={%s} fFlags=%#x cbRecv=%u cbSend=%u fIgnoreConnectFailure=%RTbool\n",
1724 pDrvIns->iInstance, OpenReq.szNetwork, OpenReq.enmTrunkType, OpenReq.szTrunk, OpenReq.fFlags,
1725 OpenReq.cbRecv, OpenReq.cbSend, fIgnoreConnectFailure));
1726
1727#ifdef RT_OS_DARWIN
1728 /* Temporary hack: attach to a network with the name 'if=en0' and you're hitting the wire. */
1729 if ( !OpenReq.szTrunk[0]
1730 && OpenReq.enmTrunkType == kIntNetTrunkType_None
1731 && !strncmp(pThis->szNetwork, RT_STR_TUPLE("if=en"))
1732 && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("if=en") - 1])
1733 && !pThis->szNetwork[sizeof("if=en")])
1734 {
1735 OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
1736 strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("if=") - 1]);
1737 }
1738 /* Temporary hack: attach to a network with the name 'wif=en0' and you're on the air. */
1739 if ( !OpenReq.szTrunk[0]
1740 && OpenReq.enmTrunkType == kIntNetTrunkType_None
1741 && !strncmp(pThis->szNetwork, RT_STR_TUPLE("wif=en"))
1742 && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("wif=en") - 1])
1743 && !pThis->szNetwork[sizeof("wif=en")])
1744 {
1745 OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
1746 OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
1747 strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("wif=") - 1]);
1748 }
1749#endif /* DARWIN */
1750
1751 /*
1752 * Create the event semaphore, S/G cache and xmit critsect.
1753 */
1754 rc = RTSemEventCreate(&pThis->hRecvEvt);
1755 if (RT_FAILURE(rc))
1756 return rc;
1757 rc = RTMemCacheCreate(&pThis->hSgCache, sizeof(PDMSCATTERGATHER), 0, UINT32_MAX, NULL, NULL, pThis, 0);
1758 if (RT_FAILURE(rc))
1759 return rc;
1760 rc = PDMDrvHlpCritSectInit(pDrvIns, &pThis->XmitLock, RT_SRC_POS, "IntNetXmit");
1761 if (RT_FAILURE(rc))
1762 return rc;
1763
1764 /*
1765 * Create the interface.
1766 */
1767 if (SUPR3IsDriverless()) /** @todo This is probably not good enough for doing fuzz testing, but later... */
1768 return PDMDrvHlpVMSetError(pDrvIns, VERR_SUP_DRIVERLESS, RT_SRC_POS,
1769 N_("Cannot attach to '%s' in driverless mode"), pThis->szNetwork);
1770 OpenReq.hIf = INTNET_HANDLE_INVALID;
1771 rc = PDMDrvHlpSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_OPEN, &OpenReq, sizeof(OpenReq));
1772 if (RT_FAILURE(rc))
1773 {
1774 if (fIgnoreConnectFailure)
1775 {
1776 /*
1777 * During VM restore it is fatal if the network is not available because the
1778 * VM settings are locked and the user has no chance to fix network settings.
1779 * Therefore don't abort but just raise a runtime warning.
1780 */
1781 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "HostIfNotConnecting",
1782 N_ ("Cannot connect to the network interface '%s'. The virtual "
1783 "network card will appear to work but the guest will not "
1784 "be able to connect. Please choose a different network in the "
1785 "network settings"), OpenReq.szTrunk);
1786
1787 return VERR_PDM_NO_ATTACHED_DRIVER;
1788 }
1789 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1790 N_("Failed to open/create the internal network '%s'"), pThis->szNetwork);
1791 }
1792
1793 AssertRelease(OpenReq.hIf != INTNET_HANDLE_INVALID);
1794 pThis->hIf = OpenReq.hIf;
1795 Log(("IntNet%d: hIf=%RX32 '%s'\n", pDrvIns->iInstance, pThis->hIf, pThis->szNetwork));
1796
1797 /*
1798 * Get default buffer.
1799 */
1800 INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
1801 GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
1802 GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
1803 GetBufferPtrsReq.pSession = NIL_RTR0PTR;
1804 GetBufferPtrsReq.hIf = pThis->hIf;
1805 GetBufferPtrsReq.pRing3Buf = NULL;
1806 GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
1807 rc = PDMDrvHlpSUPCallVMMR0Ex(pDrvIns, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, &GetBufferPtrsReq, sizeof(GetBufferPtrsReq));
1808 if (RT_FAILURE(rc))
1809 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1810 N_("Failed to get ring-3 buffer for the newly created interface to '%s'"), pThis->szNetwork);
1811 AssertRelease(RT_VALID_PTR(GetBufferPtrsReq.pRing3Buf));
1812 pThis->pBufR3 = GetBufferPtrsReq.pRing3Buf;
1813#ifdef VBOX_WITH_DRVINTNET_IN_R0
1814 pThis->pBufR0 = GetBufferPtrsReq.pRing0Buf;
1815#endif
1816
1817 /*
1818 * Register statistics.
1819 */
1820 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->pBufR3->Recv.cbStatWritten, "Bytes/Received", STAMUNIT_BYTES, "Number of received bytes.");
1821 PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->pBufR3->Send.cbStatWritten, "Bytes/Sent", STAMUNIT_BYTES, "Number of sent bytes.");
1822 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Recv.cOverflows, "Overflows/Recv", "Number overflows.");
1823 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Send.cOverflows, "Overflows/Sent", "Number overflows.");
1824 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Recv.cStatFrames, "Packets/Received", "Number of received packets.");
1825 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Send.cStatFrames, "Packets/Sent", "Number of sent packets.");
1826 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatReceivedGso, "Packets/Received-Gso", "The GSO portion of the received packets.");
1827 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatSentGso, "Packets/Sent-Gso", "The GSO portion of the sent packets.");
1828 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatSentR0, "Packets/Sent-R0", "The ring-0 portion of the sent packets.");
1829
1830 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatLost, "Packets/Lost", "Number of lost packets.");
1831 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatYieldsNok, "YieldOk", "Number of times yielding helped fix an overflow.");
1832 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatYieldsOk, "YieldNok", "Number of times yielding didn't help fix an overflow.");
1833 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatBadFrames, "BadFrames", "Number of bad frames seed by the consumers.");
1834 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatSend1, "Send1", "Profiling IntNetR0IfSend.");
1835 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatSend2, "Send2", "Profiling sending to the trunk.");
1836 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatRecv1, "Recv1", "Reserved for future receive profiling.");
1837 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatRecv2, "Recv2", "Reserved for future receive profiling.");
1838 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatReserved, "Reserved", "Reserved for future use.");
1839#ifdef VBOX_WITH_STATISTICS
1840 PDMDrvHlpSTAMRegProfileAdv(pDrvIns, &pThis->StatReceive, "Receive", "Profiling packet receive runs.");
1841 PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->StatTransmit, "Transmit", "Profiling packet transmit runs.");
1842#endif
1843 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitWakeupR0, "XmitWakeup-R0", "Xmit thread wakeups from ring-0.");
1844 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitWakeupR3, "XmitWakeup-R3", "Xmit thread wakeups from ring-3.");
1845 PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitProcessRing, "XmitProcessRing", "Time xmit thread was told to process the ring.");
1846
1847 /*
1848 * Create the async I/O threads.
1849 * Note! Using a PDM thread here doesn't fit with the IsService=true operation.
1850 */
1851 rc = RTThreadCreate(&pThis->hRecvThread, drvR3IntNetRecvThread, pThis, 0,
1852 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "INTNET-RECV");
1853 if (RT_FAILURE(rc))
1854 {
1855 AssertRC(rc);
1856 return rc;
1857 }
1858
1859 rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hXmitEvt);
1860 AssertRCReturn(rc, rc);
1861
1862 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pXmitThread, pThis,
1863 drvR3IntNetXmitThread, drvR3IntNetXmitWakeUp, 0, RTTHREADTYPE_IO, "INTNET-XMIT");
1864 AssertRCReturn(rc, rc);
1865
1866#ifdef VBOX_WITH_DRVINTNET_IN_R0
1867 /*
1868 * Resolve the ring-0 context interface addresses.
1869 */
1870 rc = pDrvIns->pHlpR3->pfnLdrGetR0InterfaceSymbols(pDrvIns, &pThis->INetworkUpR0, sizeof(pThis->INetworkUpR0),
1871 "drvIntNetUp_", PDMINETWORKUP_SYM_LIST);
1872 AssertLogRelRCReturn(rc, rc);
1873#endif
1874
1875 /*
1876 * Activate data transmission as early as possible
1877 */
1878 if (pThis->fActivateEarlyDeactivateLate)
1879 {
1880 ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
1881 RTSemEventSignal(pThis->hRecvEvt);
1882
1883 drvR3IntNetUpdateMacAddress(pThis);
1884 drvR3IntNetSetActive(pThis, true /* fActive */);
1885 }
1886
1887 return rc;
1888}
1889
1890
1891
1892/**
1893 * Internal networking transport driver registration record.
1894 */
1895const PDMDRVREG g_DrvIntNet =
1896{
1897 /* u32Version */
1898 PDM_DRVREG_VERSION,
1899 /* szName */
1900 "IntNet",
1901 /* szRCMod */
1902 "VBoxDDRC.rc",
1903 /* szR0Mod */
1904 "VBoxDDR0.r0",
1905 /* pszDescription */
1906 "Internal Networking Transport Driver",
1907 /* fFlags */
1908#ifdef VBOX_WITH_DRVINTNET_IN_R0
1909 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0,
1910#else
1911 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1912#endif
1913 /* fClass. */
1914 PDM_DRVREG_CLASS_NETWORK,
1915 /* cMaxInstances */
1916 ~0U,
1917 /* cbInstance */
1918 sizeof(DRVINTNET),
1919 /* pfnConstruct */
1920 drvR3IntNetConstruct,
1921 /* pfnDestruct */
1922 drvR3IntNetDestruct,
1923 /* pfnRelocate */
1924 drvR3IntNetRelocate,
1925 /* pfnIOCtl */
1926 NULL,
1927 /* pfnPowerOn */
1928 drvR3IntNetPowerOn,
1929 /* pfnReset */
1930 NULL,
1931 /* pfnSuspend */
1932 drvR3IntNetSuspend,
1933 /* pfnResume */
1934 drvR3IntNetResume,
1935 /* pfnAttach */
1936 NULL,
1937 /* pfnDetach */
1938 NULL,
1939 /* pfnPowerOff */
1940 drvR3IntNetPowerOff,
1941 /* pfnSoftReset */
1942 NULL,
1943 /* u32EndVersion */
1944 PDM_DRVREG_VERSION
1945};
1946
1947#endif /* IN_RING3 */
1948
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use