VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NetLib/IntNetIfCtx.cpp@ 97071

Last change on this file since 97071 was 97071, checked in by vboxsync, 3 years ago

NetworkServices: Implement support for communicating over the R3 internal network service in driverless mode, bugref:10297

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 KB
Line 
1/* $Id: IntNetIfCtx.cpp 97071 2022-10-10 16:30:56Z 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 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
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 pvArg Pointer to the argument data.
101 * @param cbArg Size of the argument data in bytes.
102 */
103static int intnetR3IfCtxCallSvc(PINTNETIFCTXINT pThis, uint32_t uOperation, PSUPVMMR0REQHDR pReqHdr)
104{
105#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
106 if (pThis->fIntNetR3Svc)
107 {
108# if defined(RT_OS_DARWIN)
109 size_t cbReq = pReqHdr->cbReq;
110 xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
111 xpc_dictionary_set_uint64(hObj, "req-id", uOperation);
112 xpc_dictionary_set_data(hObj, "req", pReqHdr, pReqHdr->cbReq);
113 xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj);
114 int rc = (int)xpc_dictionary_get_int64(hObjReply, "rc");
115
116 size_t cbReply = 0;
117 const void *pvData = xpc_dictionary_get_data(hObjReply, "reply", &cbReply);
118 AssertRelease(cbReply == cbReq);
119 memcpy(pReqHdr, pvData, cbReq);
120 xpc_release(hObjReply);
121
122 return rc;
123# endif
124 }
125 else
126#else
127 RT_NOREF(pThis);
128#endif
129 return SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, uOperation, 0, pReqHdr);
130}
131
132
133#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
134/**
135 * Calls the internal networking switch service living in either R0 or in another R3 process.
136 *
137 * @returns VBox status code.
138 * @param pThis The internal network driver instance data.
139 * @param uOperation The operation to execute.
140 * @param pvArg Pointer to the argument data.
141 * @param cbArg Size of the argument data in bytes.
142 */
143static int intnetR3IfCtxCallSvcAsync(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 intnetR3IfCtxMapBufferPointers(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 intnetR3IfCtxClose(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 = intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq.Hdr);
226 AssertRC(rc);
227 }
228}
229
230
231DECLHIDDEN(int) IntNetR3IfCtxCreate(PINTNETIFCTX phIfCtx, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
232 const char *pszTrunk, size_t cbSend, size_t cbRecv, uint32_t fFlags)
233{
234 AssertPtrReturn(phIfCtx, VERR_INVALID_POINTER);
235 AssertPtrReturn(pszNetwork, VERR_INVALID_POINTER);
236 AssertPtrReturn(pszTrunk, VERR_INVALID_POINTER);
237
238 PSUPDRVSESSION pSession = NIL_RTR0PTR;
239 int rc = SUPR3Init(&pSession);
240 if (RT_SUCCESS(rc))
241 {
242 PINTNETIFCTXINT pThis = (PINTNETIFCTXINT)RTMemAllocZ(sizeof(*pThis));
243 if (RT_LIKELY(pThis))
244 {
245 pThis->pSupDrvSession = pSession;
246#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
247 pThis->hEvtRecv = NIL_RTSEMEVENT;
248#endif
249
250 /* Driverless operation needs support for running the internal network switch using IPC. */
251 if (SUPR3IsDriverless())
252 {
253#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
254# if defined(RT_OS_DARWIN)
255 xpc_connection_t hXpcCon = xpc_connection_create(INTNET_R3_SVC_NAME, NULL);
256 xpc_connection_set_event_handler(hXpcCon, ^(xpc_object_t hObj) {
257 if (xpc_get_type(hObj) == XPC_TYPE_ERROR)
258 {
259 /** @todo Error handling - reconnecting. */
260 }
261 else
262 {
263 /* Out of band messages should only come when there is something to receive. */
264 RTSemEventSignal(pThis->hEvtRecv);
265 }
266 });
267
268 xpc_connection_activate(hXpcCon);
269 pThis->hXpcCon = hXpcCon;
270# endif
271 pThis->fIntNetR3Svc = true;
272 rc = RTSemEventCreate(&pThis->hEvtRecv);
273#else
274 rc = VERR_SUP_DRIVERLESS;
275#endif
276 }
277 else
278 {
279 /* Need to load VMMR0.r0 containing the network switching code. */
280 char szPathVMMR0[RTPATH_MAX];
281
282 rc = RTPathExecDir(szPathVMMR0, sizeof(szPathVMMR0));
283 if (RT_SUCCESS(rc))
284 {
285 rc = RTPathAppend(szPathVMMR0, sizeof(szPathVMMR0), "VMMR0.r0");
286 if (RT_SUCCESS(rc))
287 rc = SUPR3LoadVMM(szPathVMMR0, /* :pErrInfo */ NULL);
288 }
289 }
290
291 if (RT_SUCCESS(rc))
292 {
293 /* Open the interface. */
294 INTNETOPENREQ OpenReq;
295 RT_ZERO(OpenReq);
296
297 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
298 OpenReq.Hdr.cbReq = sizeof(OpenReq);
299 OpenReq.pSession = pThis->pSupDrvSession;
300 OpenReq.enmTrunkType = enmTrunkType;
301 OpenReq.fFlags = fFlags;
302 OpenReq.cbSend = cbSend;
303 OpenReq.cbRecv = cbRecv;
304 OpenReq.hIf = INTNET_HANDLE_INVALID;
305
306 rc = RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), pszNetwork);
307 if (RT_SUCCESS(rc))
308 rc = RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), pszTrunk);
309 if (RT_SUCCESS(rc))
310 {
311 rc = intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_OPEN, &OpenReq.Hdr);
312 if (RT_SUCCESS(rc))
313 {
314 pThis->hIf = OpenReq.hIf;
315
316 rc = intnetR3IfCtxMapBufferPointers(pThis);
317 if (RT_SUCCESS(rc))
318 {
319 *phIfCtx = pThis;
320 return VINF_SUCCESS;
321 }
322 }
323
324 intnetR3IfCtxClose(pThis);
325 }
326 }
327
328#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
329 if (pThis->fIntNetR3Svc)
330 {
331# if defined(RT_OS_DARWIN)
332 if (pThis->hXpcCon)
333 xpc_connection_cancel(pThis->hXpcCon);
334 pThis->hXpcCon = NULL;
335# endif
336
337 if (pThis->hEvtRecv != NIL_RTSEMEVENT)
338 RTSemEventDestroy(pThis->hEvtRecv);
339 }
340#endif
341
342 RTMemFree(pThis);
343 }
344
345 SUPR3Term();
346 }
347
348 return rc;
349}
350
351
352DECLHIDDEN(int) IntNetR3IfCtxDestroy(INTNETIFCTX hIfCtx)
353{
354 PINTNETIFCTXINT pThis = hIfCtx;
355 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
356
357 intnetR3IfCtxClose(pThis);
358
359#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
360 if (pThis->fIntNetR3Svc)
361 {
362# if defined(RT_OS_DARWIN)
363 /* Unmap the shared buffer. */
364 munmap(pThis->pBuf, pThis->cbBuf);
365 xpc_connection_cancel(pThis->hXpcCon);
366 pThis->hXpcCon = NULL;
367# endif
368 RTSemEventDestroy(pThis->hEvtRecv);
369 pThis->fIntNetR3Svc = false;
370 }
371#endif
372
373 RTMemFree(pThis);
374 return VINF_SUCCESS;
375}
376
377
378DECLHIDDEN(int) IntNetR3IfCtxQueryBufferPtr(INTNETIFCTX hIfCtx, PINTNETBUF *ppIfBuf)
379{
380 PINTNETIFCTXINT pThis = hIfCtx;
381 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
382 AssertPtrReturn(ppIfBuf, VERR_INVALID_POINTER);
383
384 *ppIfBuf = pThis->pBuf;
385 return VINF_SUCCESS;
386}
387
388
389DECLHIDDEN(int) IntNetR3IfCtxSetActive(INTNETIFCTX hIfCtx, bool fActive)
390{
391 PINTNETIFCTXINT pThis = hIfCtx;
392 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
393
394 INTNETIFSETACTIVEREQ Req;
395 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
396 Req.Hdr.cbReq = sizeof(Req);
397 Req.pSession = pThis->pSupDrvSession;
398 Req.hIf = pThis->hIf;
399 Req.fActive = fActive;
400 return intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_ACTIVE, &Req.Hdr);
401}
402
403
404DECLHIDDEN(int) IntNetR3IfCtxSetPromiscuous(INTNETIFCTX hIfCtx, bool fPromiscuous)
405{
406 PINTNETIFCTXINT pThis = hIfCtx;
407 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
408
409 INTNETIFSETPROMISCUOUSMODEREQ Req;
410 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
411 Req.Hdr.cbReq = sizeof(Req);
412 Req.pSession = pThis->pSupDrvSession;
413 Req.hIf = pThis->hIf;
414 Req.fPromiscuous = fPromiscuous;
415 return intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req.Hdr);
416}
417
418
419DECLHIDDEN(int) IntNetR3IfSend(INTNETIFCTX hIfCtx)
420{
421 PINTNETIFCTXINT pThis = hIfCtx;
422 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
423
424 INTNETIFSENDREQ Req;
425 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
426 Req.Hdr.cbReq = sizeof(Req);
427 Req.pSession = pThis->pSupDrvSession;
428 Req.hIf = pThis->hIf;
429 return intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &Req.Hdr);
430}
431
432
433DECLHIDDEN(int) IntNetR3IfWait(INTNETIFCTX hIfCtx, uint32_t cMillies)
434{
435 PINTNETIFCTXINT pThis = hIfCtx;
436 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
437
438 int rc = VINF_SUCCESS;
439 INTNETIFWAITREQ WaitReq;
440 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
441 WaitReq.Hdr.cbReq = sizeof(WaitReq);
442 WaitReq.pSession = pThis->pSupDrvSession;
443 WaitReq.hIf = pThis->hIf;
444 WaitReq.cMillies = cMillies;
445#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
446 if (pThis->fIntNetR3Svc)
447 {
448 /* Send an asynchronous message. */
449 rc = intnetR3IfCtxCallSvcAsync(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr);
450 if (RT_SUCCESS(rc))
451 {
452 /* Wait on the receive semaphore. */
453 rc = RTSemEventWait(pThis->hEvtRecv, cMillies);
454 }
455 }
456 else
457#endif
458 rc = intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq.Hdr);
459
460 return rc;
461}
462
463
464DECLHIDDEN(int) IntNetR3IfWaitAbort(INTNETIFCTX hIfCtx)
465{
466 PINTNETIFCTXINT pThis = hIfCtx;
467 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
468
469 INTNETIFABORTWAITREQ AbortWaitReq;
470 AbortWaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
471 AbortWaitReq.Hdr.cbReq = sizeof(AbortWaitReq);
472 AbortWaitReq.pSession = pThis->pSupDrvSession;
473 AbortWaitReq.hIf = pThis->hIf;
474 AbortWaitReq.fNoMoreWaits = true;
475 return intnetR3IfCtxCallSvc(pThis, VMMR0_DO_INTNET_IF_ABORT_WAIT, &AbortWaitReq.Hdr);
476}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette