VirtualBox

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

Last change on this file since 91483 was 87900, checked in by vboxsync, 4 years ago

IntNetIf: Add ifSetPromiscuous(). bugid:9959.

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