VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NetLib/IntNetIf.cpp@ 101505

Last change on this file since 101505 was 100870, checked in by vboxsync, 16 months ago

Devices/DrvIntNet,NetworkServices/VBoxIntNetSwitch: Plug a few memory leaks, ticketref:21752

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: IntNetIf.cpp 100870 2023-08-14 12:51:17Z vboxsync $ */
2/** @file
3 * IntNetIfCtx - Abstract API implementing an IntNet connection using the R0 support driver or some R3 IPC variant.
4 */
5
6/*
7 * Copyright (C) 2022-2023 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#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
33# if defined(RT_OS_DARWIN)
34# include <xpc/xpc.h> /* This needs to be here because it drags PVM in and cdefs.h needs to undefine it... */
35# else
36# error "R3 internal networking not implemented for this platform yet!"
37# endif
38#endif
39
40#include <iprt/cdefs.h>
41#include <iprt/path.h>
42#include <iprt/semaphore.h>
43
44#include <VBox/err.h>
45#include <VBox/sup.h>
46#include <VBox/intnetinline.h>
47#include <VBox/vmm/pdmnetinline.h>
48
49#include "IntNetIf.h"
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60
61
62/**
63 * Internal network interface context instance data.
64 */
65typedef struct INTNETIFCTXINT
66{
67 /** The support driver session handle. */
68 PSUPDRVSESSION pSupDrvSession;
69 /** Interface handle. */
70 INTNETIFHANDLE hIf;
71 /** The internal network buffer. */
72 PINTNETBUF pBuf;
73#if defined (VBOX_WITH_INTNET_SERVICE_IN_R3)
74 /** Flag whether this interface is using the internal network switch in userspace path. */
75 bool fIntNetR3Svc;
76 /** Receive event semaphore. */
77 RTSEMEVENT hEvtRecv;
78# if defined(RT_OS_DARWIN)
79 /** XPC connection handle to the R3 internal network switch service. */
80 xpc_connection_t hXpcCon;
81 /** Size of the communication buffer in bytes. */
82 size_t cbBuf;
83# endif
84#endif
85} INTNETIFCTXINT;
86/** Pointer to the internal network interface context instance data. */
87typedef INTNETIFCTXINT *PINTNETIFCTXINT;
88
89
90/*********************************************************************************************************************************
91* Internal Functions *
92*********************************************************************************************************************************/
93
94/**
95 * Calls the internal networking switch service living in either R0 or in another R3 process.
96 *
97 * @returns VBox status code.
98 * @param pThis The internal network driver instance data.
99 * @param uOperation The operation to execute.
100 * @param pReqHdr Pointer to the request header.
101 */
102static int intnetR3IfCallSvc(PINTNETIFCTXINT pThis, uint32_t uOperation, PSUPVMMR0REQHDR pReqHdr)
103{
104#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
105 if (pThis->fIntNetR3Svc)
106 {
107# if defined(RT_OS_DARWIN)
108 size_t cbReq = pReqHdr->cbReq;
109 xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
110 xpc_dictionary_set_uint64(hObj, "req-id", uOperation);
111 xpc_dictionary_set_data(hObj, "req", pReqHdr, pReqHdr->cbReq);
112 xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj);
113 xpc_release(hObj);
114
115 int rc = (int)xpc_dictionary_get_int64(hObjReply, "rc");
116
117 size_t cbReply = 0;
118 const void *pvData = xpc_dictionary_get_data(hObjReply, "reply", &cbReply);
119 AssertRelease(cbReply == cbReq);
120 memcpy(pReqHdr, pvData, cbReq);
121 xpc_release(hObjReply);
122
123 return rc;
124# endif
125 }
126 else
127#else
128 RT_NOREF(pThis);
129#endif
130 return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, uOperation, 0, pReqHdr);
131}
132
133
134#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
135/**
136 * Calls the internal networking switch service living in either R0 or in another R3 process.
137 *
138 * @returns VBox status code.
139 * @param pThis The internal network driver instance data.
140 * @param uOperation The operation to execute.
141 * @param pReqHdr Pointer to the request header.
142 */
143static int intnetR3IfCallSvcAsync(PINTNETIFCTXINT pThis, uint32_t uOperation, PSUPVMMR0REQHDR pReqHdr)
144{
145 if (pThis->fIntNetR3Svc)
146 {
147 xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
148 xpc_dictionary_set_uint64(hObj, "req-id", uOperation);
149 xpc_dictionary_set_data(hObj, "req", pReqHdr, pReqHdr->cbReq);
150 xpc_connection_send_message(pThis->hXpcCon, hObj);
151 return VINF_SUCCESS;
152 }
153 else
154 return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, uOperation, 0, pReqHdr);
155}
156#endif
157
158
159/**
160 * Map the ring buffer pointer into this process R3 address space.
161 *
162 * @returns VBox status code.
163 * @param pThis The internal network driver instance data.
164 */
165static int intnetR3IfMapBufferPointers(PINTNETIFCTXINT pThis)
166{
167 int rc = VINF_SUCCESS;
168
169 INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
170 GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
171 GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
172 GetBufferPtrsReq.pSession = pThis->pSupDrvSession;
173 GetBufferPtrsReq.hIf = pThis->hIf;
174 GetBufferPtrsReq.pRing3Buf = NULL;
175 GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
176
177#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
178 if (pThis->fIntNetR3Svc)
179 {
180#if defined(RT_OS_DARWIN)
181 xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
182 xpc_dictionary_set_uint64(hObj, "req-id", VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS);
183 xpc_dictionary_set_data(hObj, "req", &GetBufferPtrsReq, sizeof(GetBufferPtrsReq));
184 xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj);
185 rc = (int)xpc_dictionary_get_int64(hObjReply, "rc");
186 if (RT_SUCCESS(rc))
187 {
188 /* Get the shared memory object. */
189 xpc_object_t hObjShMem = xpc_dictionary_get_value(hObjReply, "buf-ptr");
190 size_t cbMem = xpc_shmem_map(hObjShMem, (void **)&pThis->pBuf);
191 if (!cbMem)
192 rc = VERR_NO_MEMORY;
193 else
194 pThis->cbBuf = cbMem;
195 }
196 xpc_release(hObjReply);
197#endif
198 }
199 else
200#endif
201 {
202 rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0 /*u64Arg*/, &GetBufferPtrsReq.Hdr);
203 if (RT_SUCCESS(rc))
204 {
205 AssertRelease(RT_VALID_PTR(GetBufferPtrsReq.pRing3Buf));
206 pThis->pBuf = GetBufferPtrsReq.pRing3Buf;
207 }
208 }
209
210 return rc;
211}
212
213
214static void intnetR3IfClose(PINTNETIFCTXINT pThis)
215{
216 if (pThis->hIf != INTNET_HANDLE_INVALID)
217 {
218 INTNETIFCLOSEREQ CloseReq;
219 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
220 CloseReq.Hdr.cbReq = sizeof(CloseReq);
221 CloseReq.pSession = pThis->pSupDrvSession;
222 CloseReq.hIf = pThis->hIf;
223
224 pThis->hIf = INTNET_HANDLE_INVALID;
225 int rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq.Hdr);
226 AssertRC(rc);
227 }
228}
229
230
231DECLHIDDEN(int) IntNetR3IfCreate(PINTNETIFCTX phIfCtx, const char *pszNetwork)
232{
233 return IntNetR3IfCreateEx(phIfCtx, pszNetwork, kIntNetTrunkType_WhateverNone, "",
234 _128K /*cbSend*/, _256K /*cbRecv*/, 0 /*fFlags*/);
235}
236
237
238DECLHIDDEN(int) IntNetR3IfCreateEx(PINTNETIFCTX phIfCtx, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
239 const char *pszTrunk, uint32_t cbSend, uint32_t cbRecv, uint32_t fFlags)
240{
241 AssertPtrReturn(phIfCtx, VERR_INVALID_POINTER);
242 AssertPtrReturn(pszNetwork, VERR_INVALID_POINTER);
243 AssertPtrReturn(pszTrunk, VERR_INVALID_POINTER);
244
245 PSUPDRVSESSION pSession = NIL_RTR0PTR;
246 int rc = SUPR3Init(&pSession);
247 if (RT_SUCCESS(rc))
248 {
249 PINTNETIFCTXINT pThis = (PINTNETIFCTXINT)RTMemAllocZ(sizeof(*pThis));
250 if (RT_LIKELY(pThis))
251 {
252 pThis->pSupDrvSession = pSession;
253#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
254 pThis->hEvtRecv = NIL_RTSEMEVENT;
255#endif
256
257 /* Driverless operation needs support for running the internal network switch using IPC. */
258 if (SUPR3IsDriverless())
259 {
260#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
261# if defined(RT_OS_DARWIN)
262 xpc_connection_t hXpcCon = xpc_connection_create(INTNET_R3_SVC_NAME, NULL);
263 xpc_connection_set_event_handler(hXpcCon, ^(xpc_object_t hObj) {
264 if (xpc_get_type(hObj) == XPC_TYPE_ERROR)
265 {
266 /** @todo Error handling - reconnecting. */
267 }
268 else
269 {
270 /* Out of band messages should only come when there is something to receive. */
271 RTSemEventSignal(pThis->hEvtRecv);
272 }
273 });
274
275 xpc_connection_resume(hXpcCon);
276 pThis->hXpcCon = hXpcCon;
277# endif
278 pThis->fIntNetR3Svc = true;
279 rc = RTSemEventCreate(&pThis->hEvtRecv);
280#else
281 rc = VERR_SUP_DRIVERLESS;
282#endif
283 }
284 else
285 {
286 /* Need to load VMMR0.r0 containing the network switching code. */
287 char szPathVMMR0[RTPATH_MAX];
288
289 rc = RTPathExecDir(szPathVMMR0, sizeof(szPathVMMR0));
290 if (RT_SUCCESS(rc))
291 {
292 rc = RTPathAppend(szPathVMMR0, sizeof(szPathVMMR0), "VMMR0.r0");
293 if (RT_SUCCESS(rc))
294 rc = SUPR3LoadVMM(szPathVMMR0, /* :pErrInfo */ NULL);
295 }
296 }
297
298 if (RT_SUCCESS(rc))
299 {
300 /* Open the interface. */
301 INTNETOPENREQ OpenReq;
302 RT_ZERO(OpenReq);
303
304 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
305 OpenReq.Hdr.cbReq = sizeof(OpenReq);
306 OpenReq.pSession = pThis->pSupDrvSession;
307 OpenReq.enmTrunkType = enmTrunkType;
308 OpenReq.fFlags = fFlags;
309 OpenReq.cbSend = cbSend;
310 OpenReq.cbRecv = cbRecv;
311 OpenReq.hIf = INTNET_HANDLE_INVALID;
312
313 rc = RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), pszNetwork);
314 if (RT_SUCCESS(rc))
315 rc = RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), pszTrunk);
316 if (RT_SUCCESS(rc))
317 {
318 rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_OPEN, &OpenReq.Hdr);
319 if (RT_SUCCESS(rc))
320 {
321 pThis->hIf = OpenReq.hIf;
322
323 rc = intnetR3IfMapBufferPointers(pThis);
324 if (RT_SUCCESS(rc))
325 {
326 *phIfCtx = pThis;
327 return VINF_SUCCESS;
328 }
329 }
330
331 intnetR3IfClose(pThis);
332 }
333 }
334
335#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
336 if (pThis->fIntNetR3Svc)
337 {
338# if defined(RT_OS_DARWIN)
339 if (pThis->hXpcCon)
340 xpc_connection_cancel(pThis->hXpcCon);
341 pThis->hXpcCon = NULL;
342# endif
343
344 if (pThis->hEvtRecv != NIL_RTSEMEVENT)
345 RTSemEventDestroy(pThis->hEvtRecv);
346 }
347#endif
348
349 RTMemFree(pThis);
350 }
351
352 SUPR3Term();
353 }
354
355 return rc;
356}
357
358
359DECLHIDDEN(int) IntNetR3IfDestroy(INTNETIFCTX hIfCtx)
360{
361 PINTNETIFCTXINT pThis = hIfCtx;
362 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
363
364 intnetR3IfClose(pThis);
365
366#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
367 if (pThis->fIntNetR3Svc)
368 {
369# if defined(RT_OS_DARWIN)
370 /* Unmap the shared buffer. */
371 munmap(pThis->pBuf, pThis->cbBuf);
372 xpc_connection_cancel(pThis->hXpcCon);
373 pThis->hXpcCon = NULL;
374# endif
375 RTSemEventDestroy(pThis->hEvtRecv);
376 pThis->fIntNetR3Svc = false;
377 }
378#endif
379
380 RTMemFree(pThis);
381 return VINF_SUCCESS;
382}
383
384
385DECLHIDDEN(int) IntNetR3IfQueryBufferPtr(INTNETIFCTX hIfCtx, PINTNETBUF *ppIfBuf)
386{
387 PINTNETIFCTXINT pThis = hIfCtx;
388 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
389 AssertPtrReturn(ppIfBuf, VERR_INVALID_POINTER);
390
391 *ppIfBuf = pThis->pBuf;
392 return VINF_SUCCESS;
393}
394
395
396DECLHIDDEN(int) IntNetR3IfSetActive(INTNETIFCTX hIfCtx, bool fActive)
397{
398 PINTNETIFCTXINT pThis = hIfCtx;
399 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
400
401 INTNETIFSETACTIVEREQ Req;
402 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
403 Req.Hdr.cbReq = sizeof(Req);
404 Req.pSession = pThis->pSupDrvSession;
405 Req.hIf = pThis->hIf;
406 Req.fActive = fActive;
407 return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_ACTIVE, &Req.Hdr);
408}
409
410
411DECLHIDDEN(int) IntNetR3IfSetPromiscuous(INTNETIFCTX hIfCtx, bool fPromiscuous)
412{
413 PINTNETIFCTXINT pThis = hIfCtx;
414 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
415
416 INTNETIFSETPROMISCUOUSMODEREQ Req;
417 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
418 Req.Hdr.cbReq = sizeof(Req);
419 Req.pSession = pThis->pSupDrvSession;
420 Req.hIf = pThis->hIf;
421 Req.fPromiscuous = fPromiscuous;
422 return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req.Hdr);
423}
424
425
426DECLHIDDEN(int) IntNetR3IfSend(INTNETIFCTX hIfCtx)
427{
428 PINTNETIFCTXINT pThis = hIfCtx;
429 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
430
431 INTNETIFSENDREQ Req;
432 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
433 Req.Hdr.cbReq = sizeof(Req);
434 Req.pSession = pThis->pSupDrvSession;
435 Req.hIf = pThis->hIf;
436 return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &Req.Hdr);
437}
438
439
440DECLHIDDEN(int) IntNetR3IfWait(INTNETIFCTX hIfCtx, uint32_t cMillies)
441{
442 PINTNETIFCTXINT pThis = hIfCtx;
443 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
444
445 int rc = VINF_SUCCESS;
446 INTNETIFWAITREQ WaitReq;
447 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
448 WaitReq.Hdr.cbReq = sizeof(WaitReq);
449 WaitReq.pSession = pThis->pSupDrvSession;
450 WaitReq.hIf = pThis->hIf;
451 WaitReq.cMillies = cMillies;
452#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
453 if (pThis->fIntNetR3Svc)
454 {
455 /* Send an asynchronous message. */
456 rc = intnetR3IfCallSvcAsync(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr);
457 if (RT_SUCCESS(rc))
458 {
459 /* Wait on the receive semaphore. */
460 rc = RTSemEventWait(pThis->hEvtRecv, cMillies);
461 }
462 }
463 else
464#endif
465 rc = intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr);
466
467 return rc;
468}
469
470
471DECLHIDDEN(int) IntNetR3IfWaitAbort(INTNETIFCTX hIfCtx)
472{
473 PINTNETIFCTXINT pThis = hIfCtx;
474 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
475
476 INTNETIFABORTWAITREQ AbortWaitReq;
477 AbortWaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
478 AbortWaitReq.Hdr.cbReq = sizeof(AbortWaitReq);
479 AbortWaitReq.pSession = pThis->pSupDrvSession;
480 AbortWaitReq.hIf = pThis->hIf;
481 AbortWaitReq.fNoMoreWaits = true;
482 return intnetR3IfCallSvc(pThis, VMMR0_DO_INTNET_IF_ABORT_WAIT, &AbortWaitReq.Hdr);
483}
484
485
486DECLHIDDEN(int) IntNetR3IfPumpPkts(INTNETIFCTX hIfCtx, PFNINPUT pfnInput, void *pvUser,
487 PFNINPUTGSO pfnInputGso, void *pvUserGso)
488{
489 PINTNETIFCTXINT pThis = hIfCtx;
490 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
491 AssertPtrReturn(pfnInput, VERR_INVALID_POINTER);
492
493 int rc;
494 for (;;)
495 {
496 rc = IntNetR3IfWait(hIfCtx, RT_INDEFINITE_WAIT);
497 if (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED || rc == VERR_TIMEOUT)
498 {
499 PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&pThis->pBuf->Recv);
500 while (pHdr)
501 {
502 const uint8_t u8Type = pHdr->u8Type;
503 void *pvSegFrame;
504 uint32_t cbSegFrame;
505
506 if (u8Type == INTNETHDR_TYPE_FRAME)
507 {
508 pvSegFrame = IntNetHdrGetFramePtr(pHdr, pThis->pBuf);
509 cbSegFrame = pHdr->cbFrame;
510
511 /* pass the frame to the user callback */
512 pfnInput(pvUser, pvSegFrame, cbSegFrame);
513 }
514 else if (u8Type == INTNETHDR_TYPE_GSO)
515 {
516 size_t cbGso = pHdr->cbFrame;
517 size_t cbFrame = cbGso - sizeof(PDMNETWORKGSO);
518
519 PCPDMNETWORKGSO pcGso = IntNetHdrGetGsoContext(pHdr, pThis->pBuf);
520 if (PDMNetGsoIsValid(pcGso, cbGso, cbFrame))
521 {
522 if (pfnInputGso != NULL)
523 {
524 /* pass the frame to the user GSO input callback if set */
525 pfnInputGso(pvUserGso, pcGso, (uint32_t)cbFrame);
526 }
527 else
528 {
529 const uint32_t cSegs = PDMNetGsoCalcSegmentCount(pcGso, cbFrame);
530 for (uint32_t i = 0; i < cSegs; ++i)
531 {
532 uint8_t abHdrScratch[256];
533 pvSegFrame = PDMNetGsoCarveSegmentQD(pcGso, (uint8_t *)(pcGso + 1), cbFrame,
534 abHdrScratch,
535 i, cSegs,
536 &cbSegFrame);
537
538 /* pass carved frames to the user input callback */
539 pfnInput(pvUser, pvSegFrame, (uint32_t)cbSegFrame);
540 }
541 }
542 }
543 }
544
545 /* advance to the next input frame */
546 IntNetRingSkipFrame(&pThis->pBuf->Recv);
547 pHdr = IntNetRingGetNextFrameToRead(&pThis->pBuf->Recv);
548 }
549 }
550 else
551 break;
552 }
553 return rc;
554}
555
556
557DECLHIDDEN(int) IntNetR3IfQueryOutputFrame(INTNETIFCTX hIfCtx, uint32_t cbFrame, PINTNETFRAME pFrame)
558{
559 PINTNETIFCTXINT pThis = hIfCtx;
560 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
561
562 return IntNetRingAllocateFrame(&pThis->pBuf->Send, cbFrame, &pFrame->pHdr, &pFrame->pvFrame);
563}
564
565
566DECLHIDDEN(int) IntNetR3IfOutputFrameCommit(INTNETIFCTX hIfCtx, PCINTNETFRAME pFrame)
567{
568 PINTNETIFCTXINT pThis = hIfCtx;
569 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
570
571 IntNetRingCommitFrame(&pThis->pBuf->Send, pFrame->pHdr);
572 return IntNetR3IfSend(hIfCtx);
573}
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