VirtualBox

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

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: IntNetIf.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IntNetIf - Convenience class implementing an IntNet connection.
4 */
5
6/*
7 * Copyright (C) 2009-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#include "IntNetIf.h"
29
30#include <iprt/path.h>
31
32#include <VBox/intnetinline.h>
33#include <VBox/vmm/pdmnetinline.h>
34
35#define CALL_VMMR0(op, req) \
36 (SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, (op), 0, &(req).Hdr))
37
38
39
40IntNetIf::IntNetIf()
41 : m_pSession(NIL_RTR0PTR),
42 m_hIf(INTNET_HANDLE_INVALID),
43 m_pIfBuf(NULL),
44 m_pfnInput(NULL),
45 m_pvUser(NULL),
46 m_pfnInputGSO(NULL),
47 m_pvUserGSO(NULL)
48{
49 return;
50}
51
52
53IntNetIf::~IntNetIf()
54{
55 uninit();
56}
57
58
59
60/*
61 * SUPDrv and VMM initialization and finalization.
62 */
63
64int
65IntNetIf::r3Init()
66{
67 AssertReturn(m_pSession == NIL_RTR0PTR, VERR_GENERAL_FAILURE);
68
69 int rc = SUPR3Init(&m_pSession);
70 return rc;
71}
72
73
74void
75IntNetIf::r3Fini()
76{
77 if (m_pSession == NIL_RTR0PTR)
78 return;
79
80 SUPR3Term();
81 m_pSession = NIL_RTR0PTR;
82}
83
84
85int
86IntNetIf::vmmInit()
87{
88 char szPathVMMR0[RTPATH_MAX];
89 int rc;
90
91 rc = RTPathExecDir(szPathVMMR0, sizeof(szPathVMMR0));
92 if (RT_FAILURE(rc))
93 return rc;
94
95 rc = RTPathAppend(szPathVMMR0, sizeof(szPathVMMR0), "VMMR0.r0");
96 if (RT_FAILURE(rc))
97 return rc;
98
99 rc = SUPR3LoadVMM(szPathVMMR0, /* :pErrInfo */ NULL);
100 return rc;
101}
102
103
104
105/*
106 * Wrappers for VMM ioctl requests and low-level intnet operations.
107 */
108
109/**
110 * Open the specified internal network.
111 * Perform VMMR0_DO_INTNET_OPEN.
112 *
113 * @param strNetwork The name of the network.
114 * @param enmTrunkType The trunk type.
115 * @param strTrunk The trunk name, its meaning is specific to the type.
116 * @return iprt status code.
117 */
118int
119IntNetIf::ifOpen(const RTCString &strNetwork,
120 INTNETTRUNKTYPE enmTrunkType,
121 const RTCString &strTrunk)
122{
123 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
124 AssertReturn(m_hIf == INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
125
126 INTNETOPENREQ OpenReq;
127 RT_ZERO(OpenReq);
128
129 OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
130 OpenReq.Hdr.cbReq = sizeof(OpenReq);
131 OpenReq.pSession = m_pSession;
132
133 int rc = RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), strNetwork.c_str());
134 AssertRCReturn(rc, rc);
135
136 rc = RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), strTrunk.c_str());
137 AssertRCReturn(rc, rc);
138
139 if (enmTrunkType != kIntNetTrunkType_Invalid)
140 OpenReq.enmTrunkType = enmTrunkType;
141 else
142 OpenReq.enmTrunkType = kIntNetTrunkType_WhateverNone;
143
144 OpenReq.fFlags = 0;
145 OpenReq.cbSend = _128K;
146 OpenReq.cbRecv = _256K;
147
148 OpenReq.hIf = INTNET_HANDLE_INVALID;
149
150 rc = CALL_VMMR0(VMMR0_DO_INTNET_OPEN, OpenReq);
151 if (RT_FAILURE(rc))
152 return rc;
153
154 m_hIf = OpenReq.hIf;
155 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
156
157 return VINF_SUCCESS;
158}
159
160
161/**
162 * Set promiscuous mode on the interface.
163 */
164int
165IntNetIf::ifSetPromiscuous(bool fPromiscuous)
166{
167 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
168 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
169
170 INTNETIFSETPROMISCUOUSMODEREQ SetPromiscuousModeReq;
171 int rc;
172
173 SetPromiscuousModeReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
174 SetPromiscuousModeReq.Hdr.cbReq = sizeof(SetPromiscuousModeReq);
175 SetPromiscuousModeReq.pSession = m_pSession;
176 SetPromiscuousModeReq.hIf = m_hIf;
177
178 SetPromiscuousModeReq.fPromiscuous = fPromiscuous;
179
180 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, SetPromiscuousModeReq);
181 if (RT_FAILURE(rc))
182 return rc;
183
184 return VINF_SUCCESS;
185}
186
187
188/**
189 * Obtain R3 send/receive ring buffers for the internal network.
190 * Performs VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS.
191 * @return iprt status code.
192 */
193int
194IntNetIf::ifGetBuf()
195{
196 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
197 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
198 AssertReturn(m_pIfBuf == NULL, VERR_GENERAL_FAILURE);
199
200 INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
201 int rc;
202
203 GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
204 GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
205 GetBufferPtrsReq.pSession = m_pSession;
206 GetBufferPtrsReq.hIf = m_hIf;
207
208 GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
209 GetBufferPtrsReq.pRing3Buf = NULL;
210
211 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, GetBufferPtrsReq);
212 if (RT_FAILURE(rc))
213 return rc;
214
215 m_pIfBuf = GetBufferPtrsReq.pRing3Buf;
216 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
217
218 return VINF_SUCCESS;
219}
220
221
222/**
223 * Activate the network interface.
224 * Performs VMMR0_DO_INTNET_IF_SET_ACTIVE.
225 * @return iprt status code.
226 */
227int
228IntNetIf::ifActivate()
229{
230 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
231 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
232 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
233
234 INTNETIFSETACTIVEREQ ActiveReq;
235 int rc;
236
237 ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
238 ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
239 ActiveReq.pSession = m_pSession;
240 ActiveReq.hIf = m_hIf;
241
242 ActiveReq.fActive = 1;
243
244 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_SET_ACTIVE, ActiveReq);
245 return rc;
246}
247
248
249/**
250 * Wait for input frame(s) to become available in the receive ring
251 * buffer. Performs VMMR0_DO_INTNET_IF_WAIT.
252 *
253 * @param cMillies Timeout, defaults to RT_INDEFINITE_WAIT.
254 * @return iprt status code.
255 */
256int
257IntNetIf::ifWait(uint32_t cMillies)
258{
259 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
260 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
261
262 INTNETIFWAITREQ WaitReq;
263 int rc;
264
265 WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
266 WaitReq.Hdr.cbReq = sizeof(WaitReq);
267 WaitReq.pSession = m_pSession;
268 WaitReq.hIf = m_hIf;
269
270 WaitReq.cMillies = cMillies;
271
272 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_WAIT, WaitReq);
273 return rc;
274}
275
276
277/**
278 * Abort pending ifWait(), prevent any further attempts to wait.
279 */
280int
281IntNetIf::ifAbort()
282{
283 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
284 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
285
286 INTNETIFABORTWAITREQ AbortReq;
287 int rc;
288
289 AbortReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
290 AbortReq.Hdr.cbReq = sizeof(AbortReq);
291 AbortReq.pSession = m_pSession;
292 AbortReq.hIf = m_hIf;
293
294 AbortReq.fNoMoreWaits = true;
295
296 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_ABORT_WAIT, AbortReq);
297 return rc;
298}
299
300
301/**
302 * Process input available in the receive ring buffer.
303 * Feeds input frames to the user callback.
304 * @return iprt status code.
305 */
306int
307IntNetIf::ifProcessInput()
308{
309 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
310 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
311 AssertReturn(m_pIfBuf != NULL, VERR_GENERAL_FAILURE);
312 AssertReturn(m_pfnInput != NULL, VERR_GENERAL_FAILURE);
313
314 PCINTNETHDR pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
315 while (pHdr)
316 {
317 const uint8_t u8Type = pHdr->u8Type;
318 void *pvSegFrame;
319 uint32_t cbSegFrame;
320
321 if (u8Type == INTNETHDR_TYPE_FRAME)
322 {
323 pvSegFrame = IntNetHdrGetFramePtr(pHdr, m_pIfBuf);
324 cbSegFrame = pHdr->cbFrame;
325
326 /* pass the frame to the user callback */
327 (*m_pfnInput)(m_pvUser, pvSegFrame, cbSegFrame);
328 }
329 else if (u8Type == INTNETHDR_TYPE_GSO)
330 {
331 size_t cbGso = pHdr->cbFrame;
332 size_t cbFrame = cbGso - sizeof(PDMNETWORKGSO);
333
334 PCPDMNETWORKGSO pcGso = IntNetHdrGetGsoContext(pHdr, m_pIfBuf);
335 if (PDMNetGsoIsValid(pcGso, cbGso, cbFrame))
336 {
337 if (m_pfnInputGSO != NULL)
338 {
339 /* pass the frame to the user GSO input callback if set */
340 (*m_pfnInputGSO)(m_pvUserGSO, pcGso, (uint32_t)cbFrame);
341 }
342 else
343 {
344 const uint32_t cSegs = PDMNetGsoCalcSegmentCount(pcGso, cbFrame);
345 for (uint32_t i = 0; i < cSegs; ++i)
346 {
347 uint8_t abHdrScratch[256];
348 pvSegFrame = PDMNetGsoCarveSegmentQD(pcGso, (uint8_t *)(pcGso + 1), cbFrame,
349 abHdrScratch,
350 i, cSegs,
351 &cbSegFrame);
352
353 /* pass carved frames to the user input callback */
354 (*m_pfnInput)(m_pvUser, pvSegFrame, (uint32_t)cbSegFrame);
355 }
356 }
357 }
358 }
359
360 /* advance to the next input frame */
361 IntNetRingSkipFrame(&m_pIfBuf->Recv);
362 pHdr = IntNetRingGetNextFrameToRead(&m_pIfBuf->Recv);
363 }
364
365 return VINF_SUCCESS;
366}
367
368
369/**
370 * Flush output frames from the send ring buffer to the network.
371 * Performs VMMR0_DO_INTNET_IF_SEND.
372 */
373int
374IntNetIf::ifFlush()
375{
376 AssertReturn(m_pSession != NIL_RTR0PTR, VERR_GENERAL_FAILURE);
377 AssertReturn(m_hIf != INTNET_HANDLE_INVALID, VERR_GENERAL_FAILURE);
378
379 INTNETIFSENDREQ SendReq;
380 int rc;
381
382 SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
383 SendReq.Hdr.cbReq = sizeof(SendReq);
384 SendReq.pSession = m_pSession;
385 SendReq.hIf = m_hIf;
386
387 rc = CALL_VMMR0(VMMR0_DO_INTNET_IF_SEND, SendReq);
388 return rc;
389}
390
391
392/**
393 * Close the connection to the network.
394 * Performs VMMR0_DO_INTNET_IF_CLOSE.
395 */
396int
397IntNetIf::ifClose()
398{
399 if (m_hIf == INTNET_HANDLE_INVALID)
400 return VINF_SUCCESS;
401
402 INTNETIFCLOSEREQ CloseReq;
403
404 CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
405 CloseReq.Hdr.cbReq = sizeof(CloseReq);
406 CloseReq.pSession = m_pSession;
407 CloseReq.hIf = m_hIf;
408
409 m_hIf = INTNET_HANDLE_INVALID;
410 m_pIfBuf = NULL;
411
412 CALL_VMMR0(VMMR0_DO_INTNET_IF_CLOSE, CloseReq);
413 return VINF_SUCCESS;
414}
415
416
417
418/*
419 * Public high-level user interface.
420 */
421
422/**
423 * Connect to the specified internal network.
424 *
425 * @param strNetwork The name of the network.
426 * @param enmTrunkType The trunk type. Defaults to kIntNetTrunkType_WhateverNone.
427 * @param strTrunk The trunk name, its meaning is specific to the type.
428 * Defaults to an empty string.
429 * @return iprt status code.
430 */
431int
432IntNetIf::init(const RTCString &strNetwork,
433 INTNETTRUNKTYPE enmTrunkType,
434 const RTCString &strTrunk)
435{
436 int rc;
437
438 rc = r3Init();
439 if (RT_FAILURE(rc))
440 return rc;
441
442 rc = vmmInit();
443 if (RT_FAILURE(rc))
444 return rc;
445
446 rc = ifOpen(strNetwork, enmTrunkType, strTrunk);
447 if (RT_FAILURE(rc))
448 return rc;
449
450 rc = ifGetBuf();
451 if (RT_FAILURE(rc))
452 return rc;
453
454 rc = ifActivate();
455 if (RT_FAILURE(rc))
456 return rc;
457
458 return VINF_SUCCESS;
459}
460
461
462void
463IntNetIf::uninit()
464{
465 ifClose();
466 r3Fini();
467}
468
469
470/**
471 * Set the user input callback function.
472 *
473 * @param pfnInput User input callback.
474 * @param pvUser The user specified argument to the callback.
475 * @return iprt status code.
476 */
477int
478IntNetIf::setInputCallback(PFNINPUT pfnInput, void *pvUser)
479{
480 AssertReturn(pfnInput != NULL, VERR_INVALID_STATE);
481
482 m_pfnInput = pfnInput;
483 m_pvUser = pvUser;
484 return VINF_SUCCESS;
485}
486
487
488/**
489 * Set the user GSO input callback function.
490 *
491 * @param pfnInputGSO User input callback.
492 * @param pvUserGSO The user specified argument to the callback.
493 * @return iprt status code.
494 */
495int
496IntNetIf::setInputGSOCallback(PFNINPUTGSO pfnInputGSO, void *pvUserGSO)
497{
498 AssertReturn(pfnInputGSO != NULL, VERR_INVALID_STATE);
499
500 m_pfnInputGSO = pfnInputGSO;
501 m_pvUserGSO = pvUserGSO;
502 return VINF_SUCCESS;
503}
504
505
506/**
507 * Process incoming packets forever.
508 *
509 * User call this method on its receive thread. The packets are
510 * passed to the user inpiut callbacks. If the GSO input callback is
511 * not registered, a GSO input frame is carved into normal frames and
512 * those frames are passed to the normal input callback.
513 */
514int
515IntNetIf::ifPump()
516{
517 AssertReturn(m_pfnInput != NULL, VERR_GENERAL_FAILURE);
518
519 int rc;
520 for (;;)
521 {
522 rc = ifWait();
523 if (RT_SUCCESS(rc) || rc == VERR_INTERRUPTED || rc == VERR_TIMEOUT)
524 ifProcessInput();
525 else
526 break;
527 }
528 return rc;
529}
530
531
532int
533IntNetIf::getOutputFrame(IntNetIf::Frame &rFrame, size_t cbFrame)
534{
535 int rc;
536
537 rc = IntNetRingAllocateFrame(&m_pIfBuf->Send, (uint32_t)cbFrame,
538 &rFrame.pHdr, &rFrame.pvFrame);
539 return rc;
540}
541
542
543int
544IntNetIf::ifOutput(IntNetIf::Frame &rFrame)
545{
546 int rc;
547
548 IntNetRingCommitFrame(&m_pIfBuf->Send, rFrame.pHdr);
549
550 rc = ifFlush();
551 return rc;
552}
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