VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/SrvIntNetR0.cpp@ 16684

Last change on this file since 16684 was 16183, checked in by vboxsync, 16 years ago

#3419: Added fNoPromisc parameter to pfnCreateAndConnect to prevent going promisc on WiFi.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 149.7 KB
Line 
1/* $Id: SrvIntNetR0.cpp 16183 2009-01-22 17:31:11Z vboxsync $ */
2/** @file
3 * Internal networking - The ring 0 service.
4 */
5
6/*
7 * Copyright (C) 2006-2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_SRV_INTNET
27#include <VBox/intnet.h>
28#include <VBox/sup.h>
29#include <VBox/pdm.h>
30#include <VBox/log.h>
31#include <iprt/asm.h>
32#include <iprt/alloc.h>
33#include <iprt/semaphore.h>
34#include <iprt/spinlock.h>
35#include <iprt/thread.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include <iprt/time.h>
39#include <iprt/handletable.h>
40#include <iprt/net.h>
41
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** @def INTNET_WITH_DHCP_SNOOPING
47 * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
48#define INTNET_WITH_DHCP_SNOOPING
49
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54typedef enum INTNETADDRTYPE
55{
56 /** The invalid 0 entry. */
57 kIntNetAddrType_Invalid = 0,
58 /** IP version 4. */
59 kIntNetAddrType_IPv4,
60 /** IP version 6. */
61 kIntNetAddrType_IPv6,
62 /** IPX. */
63 kIntNetAddrType_IPX,
64 /** The end of the valid values. */
65 kIntNetAddrType_End,
66 /** The usual 32-bit hack. */
67 kIntNetAddrType_32BitHack = 0x7fffffff
68} INTNETADDRTYPE;
69/** Pointer to a network layer address type. */
70typedef INTNETADDRTYPE *PINTNETADDRTYPE;
71
72
73/**
74 * Address and type.
75 */
76typedef struct INTNETADDR
77{
78 /** The address type. */
79 INTNETADDRTYPE enmType;
80 /** The address. */
81 RTNETADDRU Addr;
82} INTNETADDR;
83/** Pointer to an address. */
84typedef INTNETADDR *PINTNETADDR;
85/** Pointer to a const address. */
86typedef INTNETADDR const *PCINTNETADDR;
87
88
89/**
90 * Address cache for a specific network layer.
91 */
92typedef struct INTNETADDRCACHE
93{
94 /** Pointer to the table of addresses. */
95 uint8_t *pbEntries;
96 /** The number of valid address entries. */
97 uint8_t cEntries;
98 /** The number of allocated address entries. */
99 uint8_t cEntriesAlloc;
100 /** The address size. */
101 uint8_t cbAddress;
102 /** The size of an entry. */
103 uint8_t cbEntry;
104} INTNETADDRCACHE;
105/** Pointer to an address cache. */
106typedef INTNETADDRCACHE *PINTNETADDRCACHE;
107/** Pointer to a const address cache. */
108typedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
109
110
111/**
112 * A network interface.
113 *
114 * Unless explicitly stated, all members are protect by the network semaphore.
115 */
116typedef struct INTNETIF
117{
118 /** Pointer to the next interface.
119 * This is protected by the INTNET::FastMutex. */
120 struct INTNETIF *pNext;
121 /** The current MAC address for the interface. */
122 RTMAC Mac;
123 /** Set if the INTNET::Mac member is valid. */
124 bool fMacSet;
125 /** Set if the interface is in promiscuous mode.
126 * In promiscuous mode the interface will receive all packages except the one it's sending. */
127 bool fPromiscuous;
128 /** Whether the interface is active or not. */
129 bool fActive;
130 /** Whether someone is currently in the destructor. */
131 bool volatile fDestroying;
132 /** Number of yields done to try make the interface read pending data.
133 * We will stop yeilding when this reaches a threshold assuming that the VM is paused or
134 * that it simply isn't worth all the delay. It is cleared when a successful send has been done.
135 */
136 uint32_t cYields;
137 /** Pointer to the current exchange buffer (ring-0). */
138 PINTNETBUF pIntBuf;
139 /** Pointer to ring-3 mapping of the current exchange buffer. */
140 R3PTRTYPE(PINTNETBUF) pIntBufR3;
141 /** Pointer to the default exchange buffer for the interface. */
142 PINTNETBUF pIntBufDefault;
143 /** Pointer to ring-3 mapping of the default exchange buffer. */
144 R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
145 /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
146 RTSEMEVENT volatile Event;
147 /** Number of threads sleeping on the Event semaphore. */
148 uint32_t cSleepers;
149 /** The interface handle.
150 * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
151 * should return with the appropriate error condition. */
152 INTNETIFHANDLE volatile hIf;
153 /** Pointer to the network this interface is connected to.
154 * This is protected by the INTNET::FastMutex. */
155 struct INTNETNETWORK *pNetwork;
156 /** The session this interface is associated with. */
157 PSUPDRVSESSION pSession;
158 /** The SUPR0 object id. */
159 void *pvObj;
160 /** The network layer address cache. (Indexed by type, 0 entry isn't used.) */
161 INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
162} INTNETIF;
163/** Pointer to an internal network interface. */
164typedef INTNETIF *PINTNETIF;
165
166
167/**
168 * A trunk interface.
169 */
170typedef struct INTNETTRUNKIF
171{
172 /** The port interface we present to the component. */
173 INTNETTRUNKSWPORT SwitchPort;
174 /** The port interface we get from the component. */
175 PINTNETTRUNKIFPORT pIfPort;
176 /** The trunk mutex that serializes all calls <b>to</b> the component. */
177 RTSEMFASTMUTEX FastMutex;
178 /** Pointer to the network we're connect to.
179 * This may be NULL if we're orphaned? */
180 struct INTNETNETWORK *pNetwork;
181 /** The cached MAC address of the interface the trunk is attached to.
182 * This is for the situations where we cannot take the out-bound
183 * semaphore (the recv case) but need to make frame edits (ARP). */
184 RTMAC CachedMac;
185 /** Whether to supply physical addresses with the outbound SGs. */
186 bool volatile fPhysSG;
187 /** Set if the 'wire' is in promiscuous mode.
188 * The state of the 'host' is queried each time. */
189 bool fPromiscuousWire;
190} INTNETTRUNKIF;
191/** Pointer to a trunk interface. */
192typedef INTNETTRUNKIF *PINTNETTRUNKIF;
193
194/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
195#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
196
197
198/**
199 * Internal representation of a network.
200 */
201typedef struct INTNETNETWORK
202{
203 /** The Next network in the chain.
204 * This is protected by the INTNET::FastMutex. */
205 struct INTNETNETWORK *pNext;
206 /** List of interfaces connected to the network.
207 * This is protected by the INTNET::FastMutex. */
208 PINTNETIF pIFs;
209 /** Pointer to the trunk interface.
210 * Can be NULL if there is no trunk connection. */
211 PINTNETTRUNKIF pTrunkIF;
212 /** The network mutex.
213 * It protects everything dealing with this network. */
214 RTSEMFASTMUTEX FastMutex;
215 /** Pointer to the instance data. */
216 struct INTNET *pIntNet;
217 /** The SUPR0 object id. */
218 void *pvObj;
219 /** Pointer to the temporary buffer that is used when snooping fragmented packets.
220 * This is allocated after this structure if we're sharing the MAC address with
221 * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */
222 uint8_t *pbTmp;
223 /** Network creation flags (INTNET_OPEN_FLAGS_*). */
224 uint32_t fFlags;
225 /** The number of active interfaces (excluding the trunk). */
226 uint32_t cActiveIFs;
227 /** The length of the network name. */
228 uint8_t cchName;
229 /** The network name. */
230 char szName[INTNET_MAX_NETWORK_NAME];
231 /** The trunk type. */
232 INTNETTRUNKTYPE enmTrunkType;
233 /** The trunk name. */
234 char szTrunk[INTNET_MAX_TRUNK_NAME];
235} INTNETNETWORK;
236/** Pointer to an internal network. */
237typedef INTNETNETWORK *PINTNETNETWORK;
238
239/** The size of the buffer INTNETNETWORK::pbTmp points at. */
240#define INTNETNETWORK_TMP_SIZE 2048
241
242
243/**
244 * Internal networking instance.
245 */
246typedef struct INTNET
247{
248 /** Mutex protecting the network creation, opening and destruction.
249 * (This means all operations affecting the pNetworks list.) */
250 RTSEMFASTMUTEX FastMutex;
251 /** List of networks. Protected by INTNET::Spinlock. */
252 PINTNETNETWORK volatile pNetworks;
253 /** Handle table for the interfaces. */
254 RTHANDLETABLE hHtIfs;
255} INTNET;
256
257
258
259/*******************************************************************************
260* Internal Functions *
261*******************************************************************************/
262static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
263static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
264static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis);
265static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis);
266
267
268/**
269 * Initializes a scatter / gather buffer from a simple linear buffer.
270 *
271 * @returns Pointer to the start of the frame.
272 * @param pSG Pointer to the scatter / gather structure.
273 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
274 * @param pvFrame Pointer to the frame
275 * @param cbFrame The size of the frame.
276 */
277DECLINLINE(void) intnetR0SgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
278{
279 pSG->pvOwnerData = NULL;
280 pSG->pvUserData = NULL;
281 pSG->pvUserData2 = NULL;
282 pSG->cbTotal = cbFrame;
283 pSG->cUsers = 1;
284 pSG->fFlags = INTNETSG_FLAGS_TEMP;
285 pSG->cSegsAlloc = 1;
286 pSG->cSegsUsed = 1;
287 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
288 pSG->aSegs[0].pv = pvFrame;
289 pSG->aSegs[0].cb = cbFrame;
290}
291
292
293/**
294 * Initializes a scatter / gather buffer from a internal networking packet.
295 *
296 * @returns Pointer to the start of the frame.
297 * @param pSG Pointer to the scatter / gather structure.
298 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
299 * @param pHdr Pointer to the packet header.
300 * @param pBuf The buffer the header is within. Only used in strict builds.
301 */
302DECLINLINE(void) intnetR0SgInitFromPkt(PINTNETSG pSG, PCINTNETHDR pPktHdr, PCINTNETBUF pBuf)
303{
304 pSG->cSegsUsed = 1;
305 pSG->cbTotal = pSG->aSegs[0].cb = pPktHdr->cbFrame;
306 pSG->aSegs[0].pv = INTNETHdrGetFramePtr(pPktHdr, pBuf);
307 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
308}
309
310
311/**
312 * Worker for intnetR0SgWritePart that deals with the case where the
313 * request doesn't fit into the first segment.
314 *
315 * @returns true, unless the request or SG invalid.
316 * @param pSG The SG list to write to.
317 * @param off Where to start writing (offset into the SG).
318 * @param cb How much to write.
319 * @param pvBuf The buffer to containing the bits to write.
320 */
321static bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
322{
323 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
324 return false;
325
326 /*
327 * Skip ahead to the segment where off starts.
328 */
329 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
330 unsigned iSeg = 0;
331 while (off > pSG->aSegs[iSeg].cb)
332 {
333 off -= pSG->aSegs[iSeg++].cb;
334 AssertReturn(iSeg < cSegs, false);
335 }
336
337 /*
338 * Copy the data, hoping that it's all from one segment...
339 */
340 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
341 if (cbCanCopy >= cb)
342 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
343 else
344 {
345 /* copy the portion in the current segment. */
346 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
347 cb -= cbCanCopy;
348
349 /* copy the portions in the other segments. */
350 do
351 {
352 pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
353 iSeg++;
354 AssertReturn(iSeg < cSegs, false);
355
356 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
357 memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
358
359 cb -= cbCanCopy;
360 } while (cb > 0);
361 }
362
363 return true;
364}
365
366
367/**
368 * Writes to a part of an SG.
369 *
370 * @returns true on success, false on failure (out of bounds).
371 * @param pSG The SG list to write to.
372 * @param off Where to start writing (offset into the SG).
373 * @param cb How much to write.
374 * @param pvBuf The buffer to containing the bits to write.
375 */
376DECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
377{
378 Assert(off + cb > off);
379
380 /* The optimized case. */
381 if (RT_LIKELY( pSG->cSegsUsed == 1
382 || pSG->aSegs[0].cb >= off + cb))
383 {
384 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
385 memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
386 return true;
387 }
388 return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
389}
390
391
392/**
393 * Reads a byte from a SG list.
394 *
395 * @returns The byte on success. 0xff on failure.
396 * @param pSG The SG list to read.
397 * @param off The offset (into the SG) off the byte.
398 */
399DECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
400{
401 if (RT_LIKELY(pSG->aSegs[0].cb > off))
402 return ((uint8_t const *)pSG->aSegs[0].pv)[off];
403
404 off -= pSG->aSegs[0].cb;
405 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
406 for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
407 {
408 if (pSG->aSegs[iSeg].cb > off)
409 return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
410 off -= pSG->aSegs[iSeg].cb;
411 }
412 return false;
413}
414
415
416/**
417 * Worker for intnetR0SgReadPart that deals with the case where the
418 * requested data isn't in the first segment.
419 *
420 * @returns true, unless the SG is invalid.
421 * @param pSG The SG list to read.
422 * @param off Where to start reading (offset into the SG).
423 * @param cb How much to read.
424 * @param pvBuf The buffer to read into.
425 */
426static bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
427{
428 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
429 return false;
430
431 /*
432 * Skip ahead to the segment where off starts.
433 */
434 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
435 unsigned iSeg = 0;
436 while (off > pSG->aSegs[iSeg].cb)
437 {
438 off -= pSG->aSegs[iSeg++].cb;
439 AssertReturn(iSeg < cSegs, false);
440 }
441
442 /*
443 * Copy the data, hoping that it's all from one segment...
444 */
445 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
446 if (cbCanCopy >= cb)
447 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
448 else
449 {
450 /* copy the portion in the current segment. */
451 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
452 cb -= cbCanCopy;
453
454 /* copy the portions in the other segments. */
455 do
456 {
457 pvBuf = (uint8_t *)pvBuf + cbCanCopy;
458 iSeg++;
459 AssertReturn(iSeg < cSegs, false);
460
461 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
462 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
463
464 cb -= cbCanCopy;
465 } while (cb > 0);
466 }
467
468 return true;
469}
470
471
472/**
473 * Reads a part of an SG into a buffer.
474 *
475 * @returns true on success, false on failure (out of bounds).
476 * @param pSG The SG list to read.
477 * @param off Where to start reading (offset into the SG).
478 * @param cb How much to read.
479 * @param pvBuf The buffer to read into.
480 */
481DECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
482{
483 Assert(off + cb > off);
484
485 /* The optimized case. */
486 if (RT_LIKELY( pSG->cSegsUsed == 1
487 || pSG->aSegs[0].cb >= off + cb))
488 {
489 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
490 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
491 return true;
492 }
493 return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
494}
495
496
497/**
498 * Reads an entire SG into a fittingly size buffer.
499 *
500 * @param pSG The SG list to read.
501 * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
502 */
503DECLINLINE(void) intnetR0SgRead(PCINTNETSG pSG, void *pvBuf)
504{
505 if (pSG->cSegsUsed == 1)
506 {
507 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
508 memcpy(pvBuf, pSG->aSegs[0].pv, pSG->cbTotal);
509 }
510 else
511 {
512 uint8_t *pbDst = (uint8_t *)pvBuf;
513 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
514 for (unsigned iSeg = 0; iSeg < cSegs; iSeg++)
515 {
516 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
517 Assert(cbSeg <= pSG->cbTotal && (uintptr_t)(pbDst - (uint8_t *)pvBuf) + cbSeg <= pSG->cbTotal);
518 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
519 pbDst += cbSeg;
520 }
521 }
522}
523
524
525
526
527
528
529
530/**
531 * Retain an interface.
532 *
533 * @returns VBox status code, can assume success in most situations.
534 * @param pIf The interface instance.
535 * @param pSession The current session.
536 */
537DECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
538{
539 int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
540 AssertRCReturn(rc, rc);
541 return VINF_SUCCESS;
542}
543
544
545/**
546 * Release an interface previously retained by intnetR0IfRetain or
547 * by handle lookup/freeing.
548 *
549 * @returns true if destroyed, false if not.
550 * @param pIf The interface instance.
551 * @param pSession The current session.
552 */
553DECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
554{
555 int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
556 AssertRC(rc);
557 return rc == VINF_OBJECT_DESTROYED;
558}
559
560
561/**
562 * RTHandleCreateEx callback that retains an object in the
563 * handle table before returning it.
564 *
565 * (Avoids racing the freeing of the handle.)
566 *
567 * @returns VBox status code.
568 * @param hHandleTable The handle table (ignored).
569 * @param pvObj The object (INTNETIF).
570 * @param pvCtx The context (SUPDRVSESSION).
571 * @param pvUser The user context (ignored).
572 */
573static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
574{
575 NOREF(pvUser);
576 NOREF(hHandleTable);
577 PINTNETIF pIf = (PINTNETIF)pvObj;
578 if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
579 return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
580 return VINF_SUCCESS;
581}
582
583
584
585
586
587
588/**
589 * Checks if the IPv4 address is a broadcast address.
590 * @returns true/false.
591 * @param Addr The address, network endian.
592 */
593DECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
594{
595 /* Just check for 255.255.255.255 atm. */
596 return Addr.u == UINT32_MAX;
597}
598
599
600/**
601 * Checks if the IPv4 address is a good interface address.
602 * @returns true/false.
603 * @param Addr The address, network endian.
604 */
605DECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
606{
607 /* Usual suspects. */
608 if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
609 || Addr.au8[0] == 0) /* Current network, can be used as source address. */
610 return false;
611
612 /* Unusual suspects. */
613 if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
614 || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
615 ))
616 return false;
617 return true;
618}
619
620
621/**
622 * Gets the address size of a network layer type.
623 *
624 * @returns size in bytes.
625 * @param enmType The type.
626 */
627DECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
628{
629 switch (enmType)
630 {
631 case kIntNetAddrType_IPv4: return 4;
632 case kIntNetAddrType_IPv6: return 16;
633 case kIntNetAddrType_IPX: return 4 + 6;
634 default: AssertFailedReturn(0);
635 }
636}
637
638
639/**
640 * Compares two address to see if they are equal, assuming naturally align structures.
641 *
642 * @returns true if equal, false if not.
643 * @param pAddr1 The first address.
644 * @param pAddr2 The second address.
645 * @param cbAddr The address size.
646 */
647DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
648{
649 switch (cbAddr)
650 {
651 case 4: /* IPv4 */
652 return pAddr1->au32[0] == pAddr2->au32[0];
653 case 16: /* IPv6 */
654 return pAddr1->au64[0] == pAddr2->au64[0]
655 && pAddr1->au64[1] == pAddr2->au64[1];
656 case 10: /* IPX */
657 return pAddr1->au64[0] == pAddr2->au64[0]
658 && pAddr1->au16[4] == pAddr2->au16[4];
659 default:
660 AssertFailedReturn(false);
661 }
662}
663
664
665/**
666 * Worker for intnetR0IfAddrCacheLookup that performs the lookup
667 * in the remaining cache entries after the caller has check the
668 * most likely ones.
669 *
670 * @returns -1 if not found, the index of the cache entry if found.
671 * @param pCache The cache.
672 * @param pAddr The address.
673 * @param cbAddr The address size (optimization).
674 */
675static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
676{
677 unsigned i = pCache->cEntries - 2;
678 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
679 while (i >= 1)
680 {
681 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
682 return i;
683 pbEntry -= pCache->cbEntry;
684 i--;
685 }
686
687 return -1;
688}
689
690/**
691 * Lookup an address in a cache without any expectations.
692 *
693 * @returns -1 if not found, the index of the cache entry if found.
694 * @param pCache The cache.
695 * @param pAddr The address.
696 * @param cbAddr The address size (optimization).
697 */
698DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
699{
700 Assert(pCache->cbAddress == cbAddr);
701
702 /*
703 * The optimized case is when there is one cache entry and
704 * it doesn't match.
705 */
706 unsigned i = pCache->cEntries;
707 if ( i > 0
708 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
709 return 0;
710 if (i <= 1)
711 return -1;
712
713 /*
714 * Check the last entry.
715 */
716 i--;
717 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
718 return i;
719 if (i <= 1)
720 return -1;
721
722 return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
723}
724
725
726/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
727DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
728{
729 /** @todo implement this. */
730 return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
731}
732
733
734/**
735 * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
736 * the lookup in the remaining cache entries after the caller
737 * has check the most likely ones.
738 *
739 * The routine is expecting not to find the address.
740 *
741 * @returns -1 if not found, the index of the cache entry if found.
742 * @param pCache The cache.
743 * @param pAddr The address.
744 * @param cbAddr The address size (optimization).
745 */
746static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
747{
748 /*
749 * Perform a full table lookup.
750 */
751 unsigned i = pCache->cEntries - 2;
752 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
753 while (i >= 1)
754 {
755 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
756 return i;
757 pbEntry -= pCache->cbEntry;
758 i--;
759 }
760
761 return -1;
762}
763
764
765/**
766 * Lookup an address in a cache expecting not to find it.
767 *
768 * @returns -1 if not found, the index of the cache entry if found.
769 * @param pCache The cache.
770 * @param pAddr The address.
771 * @param cbAddr The address size (optimization).
772 */
773DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
774{
775 Assert(pCache->cbAddress == cbAddr);
776
777 /*
778 * The optimized case is when there is one cache entry and
779 * it doesn't match.
780 */
781 unsigned i = pCache->cEntries;
782 if (RT_UNLIKELY( i > 0
783 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
784 return 0;
785 if (RT_LIKELY(i <= 1))
786 return -1;
787
788 /*
789 * Then check the last entry and return if there are just two cache entries.
790 */
791 i--;
792 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
793 return i;
794 if (i <= 1)
795 return -1;
796
797 return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
798}
799
800
801/**
802 * Deletes a specific cache entry.
803 *
804 * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
805 *
806 * @param pIf The interface (for logging).
807 * @param pCache The cache.
808 * @param iEntry The entry to delete.
809 * @param pszMsg Log message.
810 */
811static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
812{
813 AssertReturnVoid(iEntry < pCache->cEntries);
814 AssertReturnVoid(iEntry >= 0);
815#ifdef LOG_ENABLED
816 INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
817 PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
818 switch (enmAddrType)
819 {
820 case kIntNetAddrType_IPv4:
821 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
822 pIf->hIf, &pIf->Mac, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
823 break;
824 default:
825 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
826 pIf->hIf, &pIf->Mac, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
827 break;
828 }
829#endif
830
831 pCache->cEntries--;
832 if (iEntry < pCache->cEntries)
833 memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
834 pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
835 (pCache->cEntries - iEntry) * pCache->cbEntry);
836}
837
838
839/**
840 * Deletes an address from the cache, assuming it isn't actually in the cache.
841 *
842 * @param pIf The interface (for logging).
843 * @param pCache The cache.
844 * @param pAddr The address.
845 * @param cbAddr The address size (optimization).
846 */
847DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
848{
849 int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
850 if (RT_UNLIKELY(i >= 0))
851 intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
852}
853
854
855/**
856 * Deletes the address from all the interface caches.
857 *
858 * This is used to remove stale entries that has been reassigned to
859 * other machines on the network.
860 *
861 * @param pNetwork The network.
862 * @param pAddr The address.
863 * @param enmType The address type.
864 * @param cbAddr The address size (optimization).
865 * @param pszMsg Log message.
866 */
867DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
868 uint8_t const cbAddr, const char *pszMsg)
869{
870 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
871 {
872 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
873 if (RT_UNLIKELY(i >= 0))
874 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
875 }
876}
877
878
879/**
880 * Deletes the address from all the interface caches except the specified one.
881 *
882 * This is used to remove stale entries that has been reassigned to
883 * other machines on the network.
884 *
885 * @param pNetwork The network.
886 * @param pAddr The address.
887 * @param enmType The address type.
888 * @param cbAddr The address size (optimization).
889 * @param pszMsg Log message.
890 */
891DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
892 INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
893{
894 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
895 if (pIf != pIfSender)
896 {
897 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
898 if (RT_UNLIKELY(i >= 0))
899 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
900 }
901}
902
903
904/**
905 * Lookup an address on the network, returning the (first) interface
906 * having it in its address cache.
907 *
908 * @returns Pointer to the interface on success, NULL if not found.
909 * @param pNetwork The network.
910 * @param pAddr The address to lookup.
911 * @param enmType The address type.
912 * @param cbAddr The size of the address.
913 */
914DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
915{
916 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
917 {
918 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
919 if (i >= 0)
920 return pIf;
921 }
922 return NULL;
923}
924
925
926/**
927 * Adds an address to the cache, the caller is responsible for making sure it'
928 * s not already in the cache.
929 *
930 * @param pIf The interface (for logging).
931 * @param pCache The address cache.
932 * @param pAddr The address.
933 * @param pszMsg log message.
934 */
935static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
936{
937 if (!pCache->cEntriesAlloc)
938 {
939 /* Allocate the first array */
940 pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cbEntry * 16);
941 if (!pCache->pbEntries)
942 return;
943 pCache->cEntriesAlloc = 16;
944 }
945 else if (pCache->cEntries >= pCache->cEntriesAlloc)
946 {
947 bool fReplace = true;
948 if (pCache->cEntriesAlloc < 64)
949 {
950 uint8_t cEntriesAlloc = pCache->cEntriesAlloc + 16;
951 void *pvNew = RTMemRealloc(pCache->pbEntries, pCache->cbEntry * cEntriesAlloc);
952 if (pvNew)
953 {
954 pCache->pbEntries = (uint8_t *)pvNew;
955 pCache->cEntriesAlloc = cEntriesAlloc;
956 fReplace = false;
957 }
958 }
959 if (fReplace)
960 {
961 /* simple FIFO, might consider usage/ageing here... */
962 Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
963 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
964 memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
965 pCache->cEntries--;
966 }
967 }
968
969 /*
970 * Add the new entry to the end of the array.
971 */
972 uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
973 memcpy(pbEntry, pAddr, pCache->cbAddress);
974 memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
975#ifdef LOG_ENABLED
976 INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
977 switch (enmAddrType)
978 {
979 case kIntNetAddrType_IPv4:
980 Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
981 pIf->hIf, &pIf->Mac, pCache->cEntries, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
982 break;
983 default:
984 Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
985 pIf->hIf, &pIf->Mac, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
986 break;
987 }
988#endif
989 pCache->cEntries++;
990 Assert(pCache->cEntries <= pCache->cEntriesAlloc);
991}
992
993
994/**
995 * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
996 *
997 * @param pIf The interface (for logging).
998 * @param pCache The address cache.
999 * @param pAddr The address.
1000 * @param cbAddr The size of the address (optimization).
1001 * @param pszMsg Log message.
1002 */
1003static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
1004{
1005 /*
1006 * Check all but the first and last entries, the caller
1007 * has already checked those.
1008 */
1009 int i = pCache->cEntries - 2;
1010 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
1011 while (i >= 1)
1012 {
1013 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
1014 return;
1015 pbEntry += pCache->cbEntry;
1016 i--;
1017 }
1018
1019 /*
1020 * Not found, add it.
1021 */
1022 intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr, pszMsg);
1023}
1024
1025
1026/**
1027 * Adds an address to the cache if it's not already there.
1028 *
1029 * @param pIf The interface (for logging).
1030 * @param pCache The address cache.
1031 * @param pAddr The address.
1032 * @param cbAddr The size of the address (optimization).
1033 * @param pszMsg Log message.
1034 */
1035DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
1036{
1037 Assert(pCache->cbAddress == cbAddr);
1038
1039 /*
1040 * The optimized case is when the address the first or last cache entry.
1041 */
1042 unsigned i = pCache->cEntries;
1043 if (RT_LIKELY( i > 0
1044 && ( intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)
1045 || (i > 1
1046 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))) ))
1047 return;
1048 intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr, pszMsg);
1049}
1050
1051
1052#ifdef INTNET_WITH_DHCP_SNOOPING
1053
1054/**
1055 * Snoops IP assignments and releases from the DHCPv4 traffic.
1056 *
1057 * The caller is responsible for making sure this traffic between the
1058 * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet
1059 * need not be validated beyond the ports.
1060 *
1061 * @param pNetwork The network this frame was seen on.
1062 * @param pIpHdr Pointer to a valid IP header. This is for pseudo
1063 * header validation, so only the minimum header size
1064 * needs to be available and valid here.
1065 * @param pUdpHdr Pointer to the UDP header in the frame.
1066 * @param cbUdpPkt What's left of the frame when starting at the UDP header.
1067 */
1068static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
1069{
1070 /*
1071 * Check if the DHCP message is valid and get the type.
1072 */
1073 if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt))
1074 {
1075 Log6(("Bad UDP packet\n"));
1076 return;
1077 }
1078 PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
1079 uint8_t MsgType;
1080 if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
1081 {
1082 Log6(("Bad DHCP packet\n"));
1083 return;
1084 }
1085
1086#ifdef LOG_ENABLED
1087 /*
1088 * Log it.
1089 */
1090 const char *pszType = "unknown";
1091 switch (MsgType)
1092 {
1093 case RTNET_DHCP_MT_DISCOVER: pszType = "discover"; break;
1094 case RTNET_DHCP_MT_OFFER: pszType = "offer"; break;
1095 case RTNET_DHCP_MT_REQUEST: pszType = "request"; break;
1096 case RTNET_DHCP_MT_DECLINE: pszType = "decline"; break;
1097 case RTNET_DHCP_MT_ACK: pszType = "ack";break;
1098 case RTNET_DHCP_MT_NAC: pszType = "nac"; break;
1099 case RTNET_DHCP_MT_RELEASE: pszType = "release"; break;
1100 case RTNET_DHCP_MT_INFORM: pszType = "inform"; break;
1101 }
1102 Log6(("DHCP msg: %d (%s) client %.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d\n", MsgType, pszType, &pDhcp->bp_chaddr,
1103 pDhcp->bp_ciaddr.au8[0], pDhcp->bp_ciaddr.au8[1], pDhcp->bp_ciaddr.au8[2], pDhcp->bp_ciaddr.au8[3],
1104 pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
1105#endif /* LOG_EANBLED */
1106
1107 /*
1108 * Act upon the message.
1109 */
1110 switch (MsgType)
1111 {
1112#if 0
1113 case RTNET_DHCP_MT_REQUEST:
1114 /** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we
1115 * know, and add the IP to the cache. */
1116 break;
1117#endif
1118
1119
1120 /*
1121 * Lookup the interface by its MAC address and insert the IPv4 address into the cache.
1122 * Delete the old client address first, just in case it changed in a renewal.
1123 */
1124 case RTNET_DHCP_MT_ACK:
1125 if (intnetR0IPv4AddrIsGood(pDhcp->bp_yiaddr))
1126 for (PINTNETIF pCur = pNetwork->pIFs; pCur; pCur = pCur->pNext)
1127 if ( pCur->fMacSet
1128 && !memcmp(&pCur->Mac, &pDhcp->bp_chaddr, sizeof(RTMAC)))
1129 {
1130 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1131 (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
1132 intnetR0IfAddrCacheAdd(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1133 (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
1134 break;
1135 }
1136 break;
1137
1138
1139 /*
1140 * Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
1141 */
1142 case RTNET_DHCP_MT_RELEASE:
1143 {
1144 for (PINTNETIF pCur = pNetwork->pIFs; pCur; pCur = pCur->pNext)
1145 if ( pCur->fMacSet
1146 && !memcmp(&pCur->Mac, &pDhcp->bp_chaddr, sizeof(RTMAC)))
1147 {
1148 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1149 (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
1150 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1151 (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
1152 }
1153 break;
1154 }
1155 }
1156
1157}
1158
1159
1160/**
1161 * Worker for intnetR0TrunkIfSnoopAddr that takes care of what
1162 * is likely to be a DHCP message.
1163 *
1164 * The caller has already check that the UDP source and destination ports
1165 * are BOOTPS or BOOTPC.
1166 *
1167 * @param pNetwork The network this frame was seen on.
1168 * @param pSG The gather list for the frame.
1169 */
1170static void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
1171{
1172 /*
1173 * Get a pointer to a linear copy of the full packet, using the
1174 * temporary buffer if necessary.
1175 */
1176 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
1177 size_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
1178 if (pSG->cSegsUsed > 1)
1179 {
1180 cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
1181 Log6(("intnetR0TrunkIfSnoopDhcp: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
1182 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
1183 return;
1184 //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
1185 pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
1186 }
1187
1188 /*
1189 * Validate the IP header and find the UDP packet.
1190 */
1191 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR)))
1192 {
1193 Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
1194 return;
1195 }
1196 size_t cbIpHdr = pIpHdr->ip_hl * 4;
1197
1198 /*
1199 * Hand it over to the common DHCP snooper.
1200 */
1201 intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
1202}
1203
1204#endif /* INTNET_WITH_DHCP_SNOOPING */
1205
1206
1207/**
1208 * Snoops up source addresses from ARP requests and purge these
1209 * from the address caches.
1210 *
1211 * The purpose of this purging is to get rid of stale addresses.
1212 *
1213 * @param pNetwork The network this frame was seen on.
1214 * @param pSG The gather list for the frame.
1215 */
1216static void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
1217{
1218 /*
1219 * Check the minimum size first.
1220 */
1221 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
1222 return;
1223
1224 /*
1225 * Copy to temporary buffer if necessary.
1226 */
1227 size_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
1228 PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1229 if ( pSG->cSegsUsed != 1
1230 && pSG->aSegs[0].cb < cbPacket)
1231 {
1232 if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
1233 != (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
1234 && !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
1235 return;
1236 pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
1237 }
1238
1239 /*
1240 * Ignore packets which doesn't interest us or we perceive as malformed.
1241 */
1242 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1243 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1244 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1245 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1246 return;
1247 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1248 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1249 && ar_oper != RTNET_ARPOP_REPLY))
1250 {
1251 Log6(("ts-ar: op=%#x\n", ar_oper));
1252 return;
1253 }
1254
1255 /*
1256 * Delete the source address if it's OK.
1257 */
1258 if ( !(pArpIPv4->ar_sha.au8[0] & 1)
1259 && ( pArpIPv4->ar_sha.au16[0]
1260 || pArpIPv4->ar_sha.au16[1]
1261 || pArpIPv4->ar_sha.au16[2])
1262 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
1263 {
1264 Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
1265 pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
1266 intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
1267 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
1268 }
1269}
1270
1271
1272#ifdef INTNET_WITH_DHCP_SNOOPING
1273/**
1274 * Snoop up addresses from ARP and DHCP traffic from frames comming
1275 * over the trunk connection.
1276 *
1277 * The caller is responsible for do some basic filtering before calling
1278 * this function.
1279 * For IPv4 this means checking against the minimum DHCPv4 frame size.
1280 *
1281 * @param pNetwork The network.
1282 * @param pSG The SG list for the frame.
1283 * @param EtherType The Ethertype of the frame.
1284 */
1285static void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
1286{
1287 switch (EtherType)
1288 {
1289 case RTNET_ETHERTYPE_IPV4:
1290 {
1291 uint32_t cbIpHdr;
1292 uint8_t b;
1293
1294 Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
1295 if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN)
1296 {
1297 /* check if the protocol is UDP */
1298 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1299 if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
1300 return;
1301
1302 /* get the TCP header length */
1303 cbIpHdr = pIpHdr->ip_hl * 4;
1304 }
1305 else
1306 {
1307 /* check if the protocol is UDP */
1308 if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
1309 != RTNETIPV4_PROT_UDP)
1310 return;
1311
1312 /* get the TCP header length */
1313 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
1314 cbIpHdr = (b & 0x0f) * 4;
1315 }
1316 if (cbIpHdr < RTNETIPV4_MIN_LEN)
1317 return;
1318
1319 /* compare the ports. */
1320 if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + cbIpHdr + RTNETUDP_MIN_LEN)
1321 {
1322 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
1323 if ( ( RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPS
1324 && RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS)
1325 || ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPC
1326 && RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC))
1327 return;
1328 }
1329 else
1330 {
1331 /* get the lower byte of the UDP source port number. */
1332 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
1333 if ( b != RTNETIPV4_PORT_BOOTPS
1334 && b != RTNETIPV4_PORT_BOOTPC)
1335 return;
1336 uint8_t SrcPort = b;
1337 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
1338 if (b)
1339 return;
1340
1341 /* get the lower byte of the UDP destination port number. */
1342 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport) + 1);
1343 if ( b != RTNETIPV4_PORT_BOOTPS
1344 && b != RTNETIPV4_PORT_BOOTPC)
1345 return;
1346 if (b == SrcPort)
1347 return;
1348 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport));
1349 if (b)
1350 return;
1351 }
1352 intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
1353 break;
1354 }
1355
1356 case RTNET_ETHERTYPE_IPV6:
1357 {
1358 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1359 * need to be edited. Check out how NDP works... */
1360 break;
1361 }
1362
1363 case RTNET_ETHERTYPE_ARP:
1364 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
1365 break;
1366 }
1367}
1368#endif /* INTNET_WITH_DHCP_SNOOPING */
1369
1370
1371/**
1372 * Deals with an IPv4 packet.
1373 *
1374 * This will fish out the source IP address and add it to the cache.
1375 * Then it will look for DHCPRELEASE requests (?) and anything else
1376 * that we migh find useful later.
1377 *
1378 * @param pIf The interface that's sending the frame.
1379 * @param pIpHdr Pointer to the IPv4 header in the frame.
1380 * @param cbPacket The size of the packet, or more correctly the
1381 * size of the frame without the ethernet header.
1382 */
1383static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket)
1384{
1385 /*
1386 * Check the header size first to prevent access invalid data.
1387 */
1388 if (cbPacket < RTNETIPV4_MIN_LEN)
1389 return;
1390 uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
1391 if ( cbHdr < RTNETIPV4_MIN_LEN
1392 || cbPacket < cbHdr)
1393 return;
1394
1395 /*
1396 * If the source address is good (not broadcast or my network) and
1397 * not already in the address cache of the sender, add it. Validate
1398 * the IP header before adding it.
1399 */
1400 bool fValidatedIpHdr = false;
1401 RTNETADDRU Addr;
1402 Addr.IPv4 = pIpHdr->ip_src;
1403 if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
1404 && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
1405 {
1406 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1407 {
1408 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
1409 return;
1410 }
1411 intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
1412 fValidatedIpHdr = true;
1413 }
1414
1415#ifdef INTNET_WITH_DHCP_SNOOPING
1416 /*
1417 * Check for potential DHCP packets.
1418 */
1419 if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
1420 && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
1421 {
1422 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
1423 if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
1424 || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
1425 && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
1426 || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
1427 {
1428 if ( fValidatedIpHdr
1429 || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1430 intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
1431 else
1432 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
1433 }
1434 }
1435#endif /* INTNET_WITH_DHCP_SNOOPING */
1436}
1437
1438
1439/**
1440 * Snoop up source addresses from an ARP request or reply.
1441 *
1442 * @param pIf The interface that's sending the frame.
1443 * @param pHdr The ARP header.
1444 * @param cbPacket The size of the packet (migth be larger than the ARP
1445 * request 'cause of min ethernet frame size).
1446 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1447 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1448 */
1449static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
1450{
1451 /*
1452 * Ignore packets which doesn't interest us or we perceive as malformed.
1453 */
1454 if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
1455 return;
1456 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1457 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1458 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1459 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1460 return;
1461 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1462 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1463 && ar_oper != RTNET_ARPOP_REPLY))
1464 {
1465 Log6(("ar_oper=%#x\n", ar_oper));
1466 return;
1467 }
1468
1469 /*
1470 * Tag the SG as ARP IPv4 for later editing, then check for addresses
1471 * which can be removed or added to the address cache of the sender.
1472 */
1473 *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
1474
1475 if ( ar_oper == RTNET_ARPOP_REPLY
1476 && !(pArpIPv4->ar_tha.au8[0] & 1)
1477 && ( pArpIPv4->ar_tha.au16[0]
1478 || pArpIPv4->ar_tha.au16[1]
1479 || pArpIPv4->ar_tha.au16[2])
1480 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
1481 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1482 (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
1483
1484 if ( !memcmp(&pArpIPv4->ar_sha, &pIf->Mac, sizeof(RTMAC))
1485 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
1486 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1487 (PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
1488}
1489
1490
1491
1492/**
1493 * Checks packets send by a normal interface for new network
1494 * layer addresses.
1495 *
1496 * @param pIf The interface that's sending the frame.
1497 * @param pbFrame The frame.
1498 * @param cbFrame The size of the frame.
1499 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1500 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1501 */
1502static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, uint16_t *pfSgFlags)
1503{
1504 /*
1505 * Fish out the ethertype and look for stuff we can handle.
1506 */
1507 if (cbFrame <= sizeof(RTNETETHERHDR))
1508 return;
1509 cbFrame -= sizeof(RTNETETHERHDR);
1510
1511 uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
1512 switch (EtherType)
1513 {
1514 case RTNET_ETHERTYPE_IPV4:
1515 intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
1516 break;
1517#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
1518 case RTNET_ETHERTYPE_IPV6:
1519 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1520 * need to be edited. Check out how NDP works... */
1521 intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1522 break;
1523#endif
1524#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
1525 case RTNET_ETHERTYPE_IPX_1:
1526 case RTNET_ETHERTYPE_IPX_2:
1527 case RTNET_ETHERTYPE_IPX_3:
1528 intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1529 break;
1530#endif
1531 case RTNET_ETHERTYPE_ARP:
1532 intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1533 break;
1534 }
1535}
1536
1537
1538#ifdef IN_INTNET_TESTCASE
1539/**
1540 * Reads the next frame in the buffer.
1541 * The caller is responsible for ensuring that there is a valid frame in the buffer.
1542 *
1543 * @returns Size of the frame in bytes.
1544 * @param pBuf The buffer.
1545 * @param pRingBuff The ring buffer to read from.
1546 * @param pvFrame Where to put the frame. The caller is responsible for
1547 * ensuring that there is sufficient space for the frame.
1548 */
1549static unsigned intnetR0RingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
1550{
1551 Assert(pRingBuf->offRead < pBuf->cbBuf);
1552 Assert(pRingBuf->offRead >= pRingBuf->offStart);
1553 Assert(pRingBuf->offRead < pRingBuf->offEnd);
1554 uint32_t offRead = pRingBuf->offRead;
1555 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offRead);
1556 const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
1557 unsigned cb = pHdr->cbFrame;
1558 memcpy(pvFrame, pvFrameIn, cb);
1559
1560 /* skip the frame */
1561 offRead += pHdr->offFrame + cb;
1562 offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
1563 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
1564 if (offRead >= pRingBuf->offEnd)
1565 offRead = pRingBuf->offStart;
1566 ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
1567 return cb;
1568}
1569#endif /* IN_INTNET_TESTCASE */
1570
1571
1572/**
1573 * Writes a frame packet to the buffer.
1574 *
1575 * @returns VBox status code.
1576 * @param pBuf The buffer.
1577 * @param pRingBuf The ring buffer to read from.
1578 * @param pSG The gather list.
1579 * @param pNewDstMac Set the destination MAC address to the address if specified.
1580 */
1581static int intnetR0RingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
1582{
1583 /*
1584 * Validate input.
1585 */
1586 AssertPtr(pBuf);
1587 AssertPtr(pRingBuf);
1588 AssertPtr(pSG);
1589 Assert(pSG->cbTotal >= sizeof(RTMAC) * 2);
1590 uint32_t offWrite = pRingBuf->offWrite;
1591 Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
1592 uint32_t offRead = pRingBuf->offRead;
1593 Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
1594
1595 const uint32_t cb = RT_ALIGN_32(pSG->cbTotal, sizeof(INTNETHDR));
1596 if (offRead <= offWrite)
1597 {
1598 /*
1599 * Try fit it all before the end of the buffer.
1600 */
1601 if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
1602 {
1603 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1604 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1605 pHdr->cbFrame = pSG->cbTotal;
1606 pHdr->offFrame = sizeof(INTNETHDR);
1607
1608 intnetR0SgRead(pSG, pHdr + 1);
1609 if (pNewDstMac)
1610 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1611
1612 offWrite += cb + sizeof(INTNETHDR);
1613 Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
1614 if (offWrite >= pRingBuf->offEnd)
1615 offWrite = pRingBuf->offStart;
1616 Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
1617 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1618 return VINF_SUCCESS;
1619 }
1620
1621 /*
1622 * Try fit the frame at the start of the buffer.
1623 * (The header fits before the end of the buffer because of alignment.)
1624 */
1625 AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
1626 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
1627 {
1628 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1629 void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
1630 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1631 pHdr->cbFrame = pSG->cbTotal;
1632 pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
1633
1634 intnetR0SgRead(pSG, pvFrameOut);
1635 if (pNewDstMac)
1636 ((PRTNETETHERHDR)pvFrameOut)->DstMac = *pNewDstMac;
1637
1638 offWrite = pRingBuf->offStart + cb;
1639 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1640 Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
1641 return VINF_SUCCESS;
1642 }
1643 }
1644 /*
1645 * The reader is ahead of the writer, try fit it into that space.
1646 */
1647 else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
1648 {
1649 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1650 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1651 pHdr->cbFrame = pSG->cbTotal;
1652 pHdr->offFrame = sizeof(INTNETHDR);
1653
1654 intnetR0SgRead(pSG, pHdr + 1);
1655 if (pNewDstMac)
1656 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1657
1658 offWrite += cb + sizeof(INTNETHDR);
1659 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1660 Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
1661 return VINF_SUCCESS;
1662 }
1663
1664 /* (it didn't fit) */
1665 /** @todo stats */
1666 return VERR_BUFFER_OVERFLOW;
1667}
1668
1669
1670/**
1671 * Sends a frame to a specific interface.
1672 *
1673 * @param pIf The interface.
1674 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1675 * @param pSG The gather buffer which data is being sent to the interface.
1676 * @param pNewDstMac Set the destination MAC address to the address if specified.
1677 */
1678static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
1679{
1680// LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
1681 int rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1682 if (RT_SUCCESS(rc))
1683 {
1684 pIf->cYields = 0;
1685 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1686 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1687 RTSemEventSignal(pIf->Event);
1688 return;
1689 }
1690
1691 Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
1692
1693#if 0 /* This is bad stuff now as we're blocking while locking down the network.
1694 we really shouldn't delay the network traffic on the host just because
1695 some bugger isn't responding. Will have to deal with this in a different
1696 manner if required. */
1697 /*
1698 * Retry a few times, yielding the CPU in between.
1699 * But don't let a unresponsive VM harm performance, so give up after a couple of tries.
1700 */
1701 if ( pIf->fActive
1702 && pIf->cYields < 100)
1703 {
1704 unsigned cYields = 10;
1705#else
1706 /*
1707 * Scheduling hack, for unicore machines primarily.
1708 */
1709 if ( pIf->fActive
1710 && pIf->cYields < 4 /* just twice */
1711 && pIfSender /* but not if it's from the trunk */)
1712 {
1713 unsigned cYields = 2;
1714#endif
1715 while (--cYields > 0)
1716 {
1717 RTSemEventSignal(pIf->Event);
1718 RTThreadYield();
1719 rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1720 if (RT_SUCCESS(rc))
1721 {
1722 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
1723 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1724 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1725 RTSemEventSignal(pIf->Event);
1726 return;
1727 }
1728 pIf->cYields++;
1729 }
1730 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
1731 }
1732
1733 /* ok, the frame is lost. */
1734 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
1735 RTSemEventSignal(pIf->Event);
1736}
1737
1738
1739/**
1740 * Sends a frame down the trunk.
1741 *
1742 * The caller must own the network mutex, might be abandond temporarily.
1743 * The fTrunkLock parameter indicates whether the trunk lock is held.
1744 *
1745 * @param pThis The trunk.
1746 * @param pNetwork The network the frame is being sent to.
1747 * @param pIfSender The IF sending the frame. Used for MAC address checks in shared MAC mode.
1748 * @param fDst The destination flags.
1749 * @param pSG Pointer to the gather list.
1750 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1751 */
1752static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
1753 uint32_t fDst, PINTNETSG pSG, bool fTrunkLocked)
1754{
1755 /*
1756 * Quick sanity check.
1757 */
1758 AssertPtr(pThis);
1759 AssertPtr(pNetwork);
1760 AssertPtr(pSG);
1761 Assert(fDst);
1762 AssertReturnVoid(pThis->pIfPort);
1763
1764 /*
1765 * Edit the frame if we're sharing the MAC address with the host on the wire.
1766 *
1767 * If the frame is headed for both the host and the wire, we'll have to send
1768 * it to the host before making any modifications, and force the OS specific
1769 * backend to copy it. We do this by marking it as TEMP (which is always the
1770 * case right now).
1771 */
1772 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1773 && (fDst & INTNETTRUNKDIR_WIRE))
1774 {
1775 /* Dispatch it to the host before making changes. */
1776 if (fDst & INTNETTRUNKDIR_HOST)
1777 {
1778 Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
1779 intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG, fTrunkLocked);
1780 fDst &= ~INTNETTRUNKDIR_HOST;
1781 }
1782
1783 /* ASSUME frame from INTNETR0IfSend! */
1784 AssertReturnVoid(pSG->cSegsUsed == 1);
1785 AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
1786 AssertReturnVoid(fTrunkLocked);
1787 AssertReturnVoid(pIfSender);
1788 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
1789
1790 /*
1791 * Get the host mac address and update the ethernet header.
1792 *
1793 * The reason for caching it in the trunk structure is because
1794 * we cannot take the trunk out-bound semaphore when we make
1795 * edits in the intnetR0TrunkIfPortRecv path.
1796 */
1797 pThis->pIfPort->pfnGetMacAddress(pThis->pIfPort, &pThis->CachedMac);
1798 if (!memcmp(&pEthHdr->SrcMac, &pIfSender->Mac, sizeof(RTMAC)))
1799 pEthHdr->SrcMac = pThis->CachedMac;
1800
1801 /*
1802 * Deal with tags from the snooping phase.
1803 */
1804 if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
1805 {
1806 /*
1807 * APR IPv4: replace hardware (MAC) addresses because these end up
1808 * in ARP caches. So, if we don't the other machiens will
1809 * send the packets to the MAC address of the guest
1810 * instead of the one of the host, which won't work on
1811 * wireless of course...
1812 */
1813 PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
1814 if (!memcmp(&pArp->ar_sha, &pIfSender->Mac, sizeof(RTMAC)))
1815 {
1816 Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->CachedMac));
1817 pArp->ar_sha = pThis->CachedMac;
1818 }
1819 if (!memcmp(&pArp->ar_tha, &pIfSender->Mac, sizeof(RTMAC))) /* just in case... */
1820 {
1821 Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->CachedMac));
1822 pArp->ar_tha = pThis->CachedMac;
1823 }
1824 }
1825 //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
1826 //{ /// @todo move the editing into a different function
1827 //}
1828 }
1829
1830 /*
1831 * Temporarily leave the network lock while transmitting the frame.
1832 *
1833 * Note that we're relying on the out-bound lock to serialize threads down
1834 * in INTNETR0IfSend. It's theoretically possible for there to be race now
1835 * because I didn't implement async SG handling yet. Which is why we currently
1836 * require the trunk to be locked, well, one of the reasons.
1837 *
1838 * Another reason is that the intnetR0NetworkSendUnicast code may have to
1839 * call into the trunk interface component to do package switching.
1840 */
1841 AssertReturnVoid(fTrunkLocked); /* to be removed. */
1842
1843 int rc;
1844 if ( fTrunkLocked
1845 || intnetR0TrunkIfRetain(pThis))
1846 {
1847 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1848 AssertRC(rc);
1849 if (RT_SUCCESS(rc))
1850 {
1851 if ( fTrunkLocked
1852 || intnetR0TrunkIfOutLock(pThis))
1853 {
1854 rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
1855
1856 if (!fTrunkLocked)
1857 intnetR0TrunkIfOutUnlock(pThis);
1858 }
1859 else
1860 {
1861 AssertFailed();
1862 rc = VERR_SEM_DESTROYED;
1863 }
1864
1865 int rc2 = RTSemFastMutexRequest(pNetwork->FastMutex);
1866 AssertRC(rc2);
1867 }
1868
1869 if (!fTrunkLocked)
1870 intnetR0TrunkIfRelease(pThis);
1871 }
1872 else
1873 {
1874 AssertFailed();
1875 rc = VERR_SEM_DESTROYED;
1876 }
1877
1878 /** @todo failure statistics? */
1879 Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst));
1880}
1881
1882
1883/**
1884 * Edits an ARP packet arriving from the wire via the trunk connection.
1885 *
1886 * @param pNetwork The network the frame is being sent to.
1887 * @param pSG Pointer to the gather list for the frame.
1888 * The flags and data content may be updated.
1889 * @param pEthHdr Pointer to the ethernet header. This may also be
1890 * updated if it's a unicast...
1891 */
1892static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
1893{
1894 /*
1895 * Check the minimum size and get a linear copy of the thing to work on,
1896 * using the temporary buffer if necessary.
1897 */
1898 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
1899 return;
1900 PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1901 if ( pSG->cSegsUsed != 1
1902 && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
1903 {
1904 Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
1905 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
1906 return;
1907 pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
1908 pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
1909 }
1910
1911 /*
1912 * Ignore packets which doesn't interest us or we perceive as malformed.
1913 */
1914 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1915 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1916 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1917 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1918 return;
1919 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1920 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1921 && ar_oper != RTNET_ARPOP_REPLY))
1922 {
1923 Log6(("ar_oper=%#x\n", ar_oper));
1924 return;
1925 }
1926
1927 /* Tag it as ARP IPv4. */
1928 pSG->fFlags |= INTNETSG_FLAGS_ARP_IPV4;
1929
1930 /*
1931 * The thing we're interested in here is a reply to a query made by a guest
1932 * since we modified the MAC in the initial request the guest made.
1933 */
1934 if ( ar_oper == RTNET_ARPOP_REPLY
1935 && !memcmp(&pArpIPv4->ar_tha, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1936 {
1937 PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
1938 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
1939 if (pIf)
1940 {
1941 Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->Mac));
1942 pArpIPv4->ar_tha = pIf->Mac;
1943 if (!memcmp(&pEthHdr->DstMac, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1944 {
1945 Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1946 pEthHdr->DstMac = pIf->Mac;
1947 if ((void *)pEthHdr != pSG->aSegs[0].pv)
1948 intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->Mac);
1949 }
1950
1951 /* Write back the packet if we've been making changes to a buffered copy. */
1952 if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
1953 intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
1954 }
1955 }
1956}
1957
1958
1959/**
1960 * Detects and edits an DHCP packet arriving from the internal net.
1961 *
1962 * @param pNetwork The network the frame is being sent to.
1963 * @param pSG Pointer to the gather list for the frame.
1964 * The flags and data content may be updated.
1965 * @param pEthHdr Pointer to the ethernet header. This may also be
1966 * updated if it's a unicast...
1967 */
1968static void intnetR0NetworkEditDhcpFromIntNet(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
1969{
1970 /*
1971 * Check the minimum size and get a linear copy of the thing to work on,
1972 * using the temporary buffer if necessary.
1973 */
1974 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN))
1975 return;
1976 /*
1977 * Get a pointer to a linear copy of the full packet, using the
1978 * temporary buffer if necessary.
1979 */
1980 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
1981 size_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
1982 if (pSG->cSegsUsed > 1)
1983 {
1984 cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
1985 Log6(("intnetR0NetworkEditDhcpFromIntNet: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
1986 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
1987 return;
1988 //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
1989 pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
1990 }
1991
1992 /*
1993 * Validate the IP header and find the UDP packet.
1994 */
1995 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR)))
1996 {
1997 Log6(("intnetR0NetworkEditDhcpFromIntNet: bad ip header\n"));
1998 return;
1999 }
2000 size_t cbIpHdr = pIpHdr->ip_hl * 4;
2001 if ( pIpHdr->ip_p != RTNETIPV4_PROT_UDP /* DHCP is UDP. */
2002 || cbPacket < cbIpHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
2003 return;
2004
2005 size_t cbUdpPkt = cbPacket - cbIpHdr;
2006 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr);
2007 /* We are only interested in DHCP packets coming from client to server. */
2008 if ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS
2009 || RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC)
2010 return;
2011
2012 /*
2013 * Check if the DHCP message is valid and get the type.
2014 */
2015 if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt))
2016 {
2017 Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad UDP packet\n"));
2018 return;
2019 }
2020 PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
2021 uint8_t MsgType;
2022 if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
2023 {
2024 Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
2025 return;
2026 }
2027
2028 switch (MsgType)
2029 {
2030 case RTNET_DHCP_MT_DISCOVER:
2031 case RTNET_DHCP_MT_REQUEST:
2032 Log6(("intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n", MsgType, pDhcp->bp_flags));
2033 if (!(pDhcp->bp_flags & RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST)))
2034 {
2035 /* Patch flags */
2036 uint16_t uFlags = pDhcp->bp_flags | RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
2037 intnetR0SgWritePart(pSG, (uint8_t*)&pDhcp->bp_flags - (uint8_t*)pIpHdr + sizeof(RTNETETHERHDR), sizeof(uFlags), &uFlags);
2038 /* Patch UDP checksum */
2039 uint32_t uChecksum = (uint32_t)~pUdpHdr->uh_sum + RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
2040 while (uChecksum >> 16)
2041 uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
2042 uChecksum = ~uChecksum;
2043 intnetR0SgWritePart(pSG, (uint8_t*)&pUdpHdr->uh_sum - (uint8_t*)pIpHdr + sizeof(RTNETETHERHDR), sizeof(pUdpHdr->uh_sum), &uChecksum);
2044 }
2045 break;
2046 }
2047}
2048
2049
2050/**
2051 * Sends a broadcast frame.
2052 *
2053 * The caller must own the network mutex, might be abandond temporarily.
2054 * When pIfSender is not NULL, the caller must also own the trunk semaphore.
2055 *
2056 * @returns true if it's addressed to someone on the network, otherwise false.
2057 * @param pNetwork The network the frame is being sent to.
2058 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2059 * @param fSrc The source flags. This 0 if it's not from the trunk.
2060 * @param pSG Pointer to the gather list.
2061 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2062 * @param pEthHdr Pointer to the ethernet header.
2063 */
2064static bool intnetR0NetworkSendBroadcast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
2065 PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
2066{
2067 /*
2068 * Check for ARP packets from the wire since we'll have to make
2069 * modification to them if we're sharing the MAC address with the host.
2070 */
2071 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2072 && (fSrc & INTNETTRUNKDIR_WIRE)
2073 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP)
2074 intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
2075
2076 /*
2077 * Check for DHCP packets from the internal net since we'll have to set
2078 * broadcast flag in DHCP requests if we're sharing the MAC address with
2079 * the host.
2080 */
2081 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2082 && !fSrc
2083 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV4)
2084 intnetR0NetworkEditDhcpFromIntNet(pNetwork, pSG, pEthHdr);
2085
2086 /*
2087 * This is a broadcast or multicast address. For the present we treat those
2088 * two as the same - investigating multicast is left for later.
2089 *
2090 * Write the packet to all the interfaces and signal them.
2091 */
2092 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2093 if (pIf != pIfSender)
2094 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
2095
2096 /*
2097 * Unless the trunk is the origin, broadcast it to both the wire
2098 * and the host as well.
2099 */
2100 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2101 if ( pIfSender
2102 && pTrunkIf)
2103 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked);
2104
2105 /*
2106 * Snoop address info from packet orginating from the trunk connection.
2107 */
2108 else if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2109 && !pIfSender)
2110 {
2111#ifdef INTNET_WITH_DHCP_SNOOPING
2112 uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
2113 if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
2114 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
2115 || (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4)) )
2116 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
2117#else
2118 if (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4))
2119 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
2120#endif
2121 }
2122
2123 return false; /* broadcast frames are never dropped */
2124}
2125
2126
2127/**
2128 * Sends a multicast frame.
2129 *
2130 * The caller must own the network mutex, might be abandond temporarily.
2131 *
2132 * @returns true if it's addressed to someone on the network, otherwise false.
2133 * @param pNetwork The network the frame is being sent to.
2134 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2135 * @param fSrc The source flags. This 0 if it's not from the trunk.
2136 * @param pSG Pointer to the gather list.
2137 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2138 * @param pEthHdr Pointer to the ethernet header.
2139 */
2140static bool intnetR0NetworkSendMulticast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
2141{
2142 /** @todo implement multicast */
2143 return intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, pEthHdr);
2144}
2145
2146
2147/**
2148 * Sends a unicast frame using the network layer address instead
2149 * of the link layer one.
2150 *
2151 * The caller must own the network mutex, might be abandond temporarily.
2152 *
2153 * @returns true if it's addressed to someone on the network, otherwise false.
2154 * @param pNetwork The network the frame is being sent to.
2155 * @param pSG Pointer to the gather list.
2156 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2157 * @param pEthHdr Pointer to the ethernet header.
2158 */
2159static bool intnetR0NetworkSendUnicastWithSharedMac(PINTNETNETWORK pNetwork, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
2160{
2161 /*
2162 * Extract the network address from the packet.
2163 */
2164 RTNETADDRU Addr;
2165 INTNETADDRTYPE enmAddrType;
2166 uint8_t cbAddr;
2167 switch (RT_BE2H_U16(pEthHdr->EtherType))
2168 {
2169 case RTNET_ETHERTYPE_IPV4:
2170 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
2171 {
2172 Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
2173 return false;
2174 }
2175 enmAddrType = kIntNetAddrType_IPv4;
2176 cbAddr = sizeof(Addr.IPv4);
2177 Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
2178 break;
2179
2180#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
2181 case RTNET_ETHERTYPE_IPV6
2182 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
2183 {
2184 Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
2185 return false;
2186 }
2187 enmAddrType = kIntNetAddrType_IPv6;
2188 cbAddr = sizeof(Addr.IPv6);
2189 break;
2190#endif
2191#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
2192 case RTNET_ETHERTYPE_IPX_1:
2193 case RTNET_ETHERTYPE_IPX_2:
2194 case RTNET_ETHERTYPE_IPX_3:
2195 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
2196 {
2197 Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
2198 return false;
2199 }
2200 enmAddrType = kIntNetAddrType_IPX;
2201 cbAddr = sizeof(Addr.IPX);
2202 break;
2203#endif
2204
2205 /*
2206 * Treat ARP is broadcast (it shouldn't end up here normally,
2207 * so it goes last in the switch).
2208 */
2209 case RTNET_ETHERTYPE_ARP:
2210 Log6(("intnetshareduni: ARP\n"));
2211 /** @todo revisit this broadcasting of unicast ARP frames! */
2212 return intnetR0NetworkSendBroadcast(pNetwork, NULL, INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked, pEthHdr);
2213
2214 /*
2215 * Unknown packets are sent do all interfaces that are in promiscuous mode.
2216 */
2217 default:
2218 {
2219 Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
2220 if (!(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC)))
2221 {
2222 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2223 if (pIf->fPromiscuous)
2224 {
2225 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2226 intnetR0IfSend(pIf, NULL, pSG, NULL);
2227 }
2228 }
2229 return false;
2230 }
2231 }
2232
2233 /*
2234 * Send it to interfaces with matching network addresses.
2235 */
2236 bool fExactIntNetRecipient = false;
2237 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2238 {
2239 bool fIt = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmAddrType], &Addr, cbAddr) >= 0;
2240 if ( fIt
2241 || ( pIf->fPromiscuous
2242 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))))
2243 {
2244 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2245 fExactIntNetRecipient |= fIt;
2246 intnetR0IfSend(pIf, NULL, pSG, fIt ? &pIf->Mac : NULL);
2247 }
2248 }
2249
2250#ifdef INTNET_WITH_DHCP_SNOOPING
2251 /*
2252 * Perform DHCP snooping.
2253 */
2254 if ( enmAddrType == kIntNetAddrType_IPv4
2255 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
2256 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
2257#endif /* INTNET_WITH_DHCP_SNOOPING */
2258
2259 return fExactIntNetRecipient;
2260}
2261
2262
2263/**
2264 * Sends a unicast frame.
2265 *
2266 * The caller must own the network mutex, might be abandond temporarily.
2267 *
2268 * @returns true if it's addressed to someone on the network, otherwise false.
2269 * @param pNetwork The network the frame is being sent to.
2270 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2271 * @param fSrc The source flags. This 0 if it's not from the trunk.
2272 * @param pSG Pointer to the gather list.
2273 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2274 * @param pEthHdr Pointer to the ethernet header.
2275 */
2276static bool intnetR0NetworkSendUnicast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
2277{
2278 /*
2279 * Only send to the interfaces with matching a MAC address.
2280 */
2281 bool fExactIntNetRecipient = false;
2282 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2283 {
2284 bool fIt = false;
2285 if ( ( !pIf->fMacSet
2286 || (fIt = !memcmp(&pIf->Mac, &pEthHdr->DstMac, sizeof(pIf->Mac))) )
2287 || ( pIf->fPromiscuous
2288 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))
2289 && pIf != pIfSender /* promiscuous mode: omit the sender */))
2290 {
2291 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2292 fExactIntNetRecipient |= fIt;
2293 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
2294 }
2295 }
2296
2297 /*
2298 * Send it to the trunk?
2299 * If we didn't find the recipient on the internal network the
2300 * frame will hit the wire.
2301 */
2302 uint32_t fDst = 0;
2303 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2304 if ( pIfSender
2305 && pTrunkIf
2306 && pTrunkIf->pIfPort)
2307 {
2308 Assert(!fSrc);
2309
2310 /* promiscuous checks first as they are cheaper than pfnIsHostMac. */
2311 if ( pTrunkIf->fPromiscuousWire
2312 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_WIRE | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_WIRE)) )
2313 fDst |= INTNETTRUNKDIR_WIRE;
2314 if ( !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_HOST | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_HOST))
2315 && pTrunkIf->pIfPort->pfnIsPromiscuous(pTrunkIf->pIfPort) )
2316 fDst |= INTNETTRUNKDIR_HOST;
2317
2318 if ( fDst != (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
2319 && !fExactIntNetRecipient /* if you have duplicate mac addresses, you're screwed. */ )
2320 {
2321 if (pTrunkIf->pIfPort->pfnIsHostMac(pTrunkIf->pIfPort, &pEthHdr->DstMac))
2322 fDst |= INTNETTRUNKDIR_HOST;
2323 else
2324 fDst |= INTNETTRUNKDIR_WIRE;
2325 }
2326
2327 if (fDst)
2328 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, fDst, pSG, fTrunkLocked);
2329 }
2330
2331 /* log it */
2332 if ( !fExactIntNetRecipient
2333 && !fDst
2334 && ( (pEthHdr->DstMac.au8[0] == 0x08 && pEthHdr->DstMac.au8[1] == 0x00 && pEthHdr->DstMac.au8[2] == 0x27)
2335 || (pEthHdr->SrcMac.au8[0] == 0x08 && pEthHdr->SrcMac.au8[1] == 0x00 && pEthHdr->SrcMac.au8[2] == 0x27)))
2336 Log2(("Dst=%.6Rhxs ??\n", &pEthHdr->DstMac));
2337
2338 return fExactIntNetRecipient;
2339}
2340
2341
2342/**
2343 * Sends a frame.
2344 *
2345 * This function will distribute the frame to the interfaces it is addressed to.
2346 * It will also update the MAC address of the sender.
2347 *
2348 * The caller must own the network mutex.
2349 *
2350 * @returns true if it's addressed to someone on the network, otherwise false.
2351 * @param pNetwork The network the frame is being sent to.
2352 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2353 * @param fSrc The source flags. This 0 if it's not from the trunk.
2354 * @param pSG Pointer to the gather list.
2355 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2356 */
2357static bool intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked)
2358{
2359 bool fRc = false;
2360
2361 /*
2362 * Assert reality.
2363 */
2364 AssertPtr(pNetwork);
2365 AssertPtrNull(pIfSender);
2366 Assert(pIfSender ? fSrc == 0 : fSrc != 0);
2367 Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
2368 AssertPtr(pSG);
2369 Assert(pSG->cSegsUsed >= 1);
2370 Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
2371 if (pSG->cbTotal < sizeof(RTNETETHERHDR))
2372 return fRc;
2373
2374 /*
2375 * Send statistics.
2376 */
2377 if (pIfSender)
2378 {
2379 STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
2380 STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, pSG->cbTotal);
2381 }
2382
2383 /*
2384 * Get the ethernet header (might theoretically involve multiple segments).
2385 */
2386 RTNETETHERHDR EthHdr;
2387 if (pSG->aSegs[0].cb >= sizeof(EthHdr))
2388 EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
2389 else if (!intnetR0SgReadPart(pSG, 0, sizeof(EthHdr), &EthHdr))
2390 return false;
2391 if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
2392 || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
2393 || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
2394 || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
2395 || EthHdr.DstMac.au8[0] == 0xff
2396 || EthHdr.SrcMac.au8[0] == 0xff)
2397 Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
2398 &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
2399
2400 /*
2401 * Inspect the header updating the mac address of the sender in the process.
2402 */
2403 if ( pIfSender
2404 && memcmp(&EthHdr.SrcMac, &pIfSender->Mac, sizeof(pIfSender->Mac)))
2405 {
2406 /** @todo stats */
2407 Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &EthHdr.SrcMac));
2408 pIfSender->Mac = EthHdr.SrcMac;
2409 pIfSender->fMacSet = true;
2410 }
2411
2412 /*
2413 * Distribute the frame.
2414 */
2415 if ( EthHdr.DstMac.au16[0] == 0xffff /* broadcast address. */
2416 && EthHdr.DstMac.au16[1] == 0xffff
2417 && EthHdr.DstMac.au16[2] == 0xffff)
2418 fRc = intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2419 else if (RT_UNLIKELY(EthHdr.DstMac.au8[0] & 1)) /* multicast address */
2420 fRc = intnetR0NetworkSendMulticast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2421 else if ( !(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2422 || !(fSrc & INTNETTRUNKDIR_WIRE))
2423 fRc = intnetR0NetworkSendUnicast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2424 else
2425 fRc = intnetR0NetworkSendUnicastWithSharedMac(pNetwork, pSG, fTrunkLocked, &EthHdr);
2426 return fRc;
2427}
2428
2429
2430/**
2431 * Sends one or more frames.
2432 *
2433 * The function will first the frame which is passed as the optional
2434 * arguments pvFrame and cbFrame. These are optional since it also
2435 * possible to chain together one or more frames in the send buffer
2436 * which the function will process after considering it's arguments.
2437 *
2438 * @returns VBox status code.
2439 * @param pIntNet The instance data.
2440 * @param hIf The interface handle.
2441 * @param pSession The caller's session.
2442 * @param pvFrame Pointer to the frame. Optional, please don't use.
2443 * @param cbFrame Size of the frame. Optional, please don't use.
2444 */
2445INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, const void *pvFrame, unsigned cbFrame)
2446{
2447 Log5(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
2448
2449 /*
2450 * Validate input and translate the handle.
2451 */
2452 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2453 if (pvFrame && cbFrame)
2454 {
2455 AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
2456 AssertPtrReturn(pvFrame, VERR_INVALID_PARAMETER);
2457 AssertPtrReturn((uint8_t *)pvFrame + cbFrame - 1, VERR_INVALID_PARAMETER);
2458
2459 /* This is the better place to crash, probe the buffer. */
2460 ASMProbeReadBuffer(pvFrame, cbFrame);
2461 }
2462 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2463 if (!pIf)
2464 return VERR_INVALID_HANDLE;
2465
2466 /*
2467 * Lock the network. If there is a trunk retain it and grab its
2468 * out-bound lock (this requires leaving the network lock first).
2469 * Grabbing the out-bound lock here simplifies things quite a bit
2470 * later on, so while this is excessive and a bit expensive it's
2471 * not worth caring about right now.
2472 */
2473 PINTNETNETWORK pNetwork = pIf->pNetwork;
2474 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2475 if (RT_FAILURE(rc))
2476 {
2477 intnetR0IfRelease(pIf, pSession);
2478 return rc;
2479 }
2480 PINTNETTRUNKIF pTrunkIf = intnetR0TrunkIfRetain(pNetwork->pTrunkIF);
2481 if (pTrunkIf)
2482 {
2483 RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2484
2485 if (!intnetR0TrunkIfOutLock(pTrunkIf))
2486 {
2487 intnetR0TrunkIfRelease(pTrunkIf);
2488 intnetR0IfRelease(pIf, pSession);
2489 return VERR_SEM_DESTROYED;
2490 }
2491
2492 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2493 if (RT_FAILURE(rc))
2494 {
2495 intnetR0TrunkIfOutUnlock(pTrunkIf);
2496 intnetR0TrunkIfRelease(pTrunkIf);
2497 intnetR0IfRelease(pIf, pSession);
2498 return rc;
2499 }
2500 }
2501
2502 INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
2503 * with buffer sharing for some OS or service. Darwin copies everything so
2504 * I won't bother allocating and managing SGs rigth now. Sorry. */
2505
2506 /*
2507 * Process the argument.
2508 */
2509 if (pvFrame && cbFrame)
2510 {
2511 intnetR0SgInitTemp(&Sg, (void *)pvFrame, cbFrame);
2512 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2513 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvFrame, cbFrame, (uint16_t *)&Sg.fFlags);
2514 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2515 }
2516
2517 /*
2518 * Process the send buffer.
2519 */
2520 while (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
2521 {
2522 /* Send the frame if the type is sane. */
2523 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
2524 if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
2525 {
2526 void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
2527 if (pvCurFrame)
2528 {
2529 intnetR0SgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
2530 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2531 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, (uint16_t *)&Sg.fFlags);
2532 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2533 }
2534 }
2535 /* else: ignore the frame */
2536
2537 /* Skip to the next frame. */
2538 INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
2539 }
2540
2541 /*
2542 * Release the semaphore(s) and release references.
2543 */
2544 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2545 if (pTrunkIf)
2546 {
2547 intnetR0TrunkIfOutUnlock(pTrunkIf);
2548 intnetR0TrunkIfRelease(pTrunkIf);
2549 }
2550
2551 intnetR0IfRelease(pIf, pSession);
2552 return rc;
2553}
2554
2555
2556/**
2557 * VMMR0 request wrapper for INTNETR0IfSend.
2558 *
2559 * @returns see INTNETR0IfSend.
2560 * @param pIntNet The internal networking instance.
2561 * @param pSession The caller's session.
2562 * @param pReq The request packet.
2563 */
2564INTNETR0DECL(int) INTNETR0IfSendReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
2565{
2566 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2567 return VERR_INVALID_PARAMETER;
2568 return INTNETR0IfSend(pIntNet, pReq->hIf, pSession, NULL, 0);
2569}
2570
2571
2572/**
2573 * Maps the default buffer into ring 3.
2574 *
2575 * @returns VBox status code.
2576 * @param pIntNet The instance data.
2577 * @param hIf The interface handle.
2578 * @param pSession The caller's session.
2579 * @param ppRing3Buf Where to store the address of the ring-3 mapping.
2580 */
2581INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
2582{
2583 LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
2584
2585 /*
2586 * Validate input.
2587 */
2588 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2589 AssertPtrReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
2590 *ppRing3Buf = 0;
2591 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2592 if (!pIf)
2593 return VERR_INVALID_HANDLE;
2594
2595 /*
2596 * ASSUMES that only the process that created an interface can use it.
2597 * ASSUMES that we created the ring-3 mapping when selecting or
2598 * allocating the buffer.
2599 */
2600 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2601 if (RT_SUCCESS(rc))
2602 {
2603 *ppRing3Buf = pIf->pIntBufR3;
2604 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2605 }
2606
2607 intnetR0IfRelease(pIf, pSession);
2608 LogFlow(("INTNETR0IfGetRing3Buffer: returns %Rrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
2609 return rc;
2610}
2611
2612
2613/**
2614 * VMMR0 request wrapper for INTNETR0IfGetRing3Buffer.
2615 *
2616 * @returns see INTNETR0IfGetRing3Buffer.
2617 * @param pIntNet The internal networking instance.
2618 * @param pSession The caller's session.
2619 * @param pReq The request packet.
2620 */
2621INTNETR0DECL(int) INTNETR0IfGetRing3BufferReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFGETRING3BUFFERREQ pReq)
2622{
2623 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2624 return VERR_INVALID_PARAMETER;
2625 return INTNETR0IfGetRing3Buffer(pIntNet, pReq->hIf, pSession, &pReq->pRing3Buf);
2626}
2627
2628
2629/**
2630 * Gets the ring-0 address of the current buffer.
2631 *
2632 * @returns VBox status code.
2633 * @param pIntNet The instance data.
2634 * @param hIf The interface handle.
2635 * @param pSession The caller's session.
2636 * @param ppRing0Buf Where to store the address of the ring-3 mapping.
2637 */
2638INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF *ppRing0Buf)
2639{
2640 LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
2641
2642 /*
2643 * Validate input.
2644 */
2645 AssertPtrReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
2646 *ppRing0Buf = NULL;
2647 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2648 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2649 if (!pIf)
2650 return VERR_INVALID_HANDLE;
2651
2652 /*
2653 * Grab the lock and get the data.
2654 * ASSUMES that the handle isn't closed while we're here.
2655 */
2656 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2657 if (RT_SUCCESS(rc))
2658 {
2659 *ppRing0Buf = pIf->pIntBuf;
2660
2661 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2662 }
2663 intnetR0IfRelease(pIf, pSession);
2664 LogFlow(("INTNETR0IfGetRing0Buffer: returns %Rrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
2665 return rc;
2666}
2667
2668
2669#if 0
2670/**
2671 * Gets the physical addresses of the default interface buffer.
2672 *
2673 * @returns VBox status code.
2674 * @param pIntNet The instance data.
2675 * @param hIF The interface handle.
2676 * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
2677 * @param cPages
2678 */
2679INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
2680{
2681 /*
2682 * Validate input.
2683 */
2684 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2685 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
2686 AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
2687 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2688 if (!pIf)
2689 return VERR_INVALID_HANDLE;
2690
2691 /*
2692 * Grab the lock and get the data.
2693 * ASSUMES that the handle isn't closed while we're here.
2694 */
2695 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2696 if (RT_SUCCESS(rc))
2697 {
2698 /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
2699 * is no need for any extra bookkeeping here.. */
2700
2701 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2702 }
2703 intnetR0IfRelease(pIf, pSession);
2704 return VERR_NOT_IMPLEMENTED;
2705}
2706#endif
2707
2708
2709/**
2710 * Sets the promiscuous mode property of an interface.
2711 *
2712 * @returns VBox status code.
2713 * @param pIntNet The instance handle.
2714 * @param hIf The interface handle.
2715 * @param pSession The caller's session.
2716 * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
2717 */
2718INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
2719{
2720 LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
2721
2722 /*
2723 * Validate & translate input.
2724 */
2725 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2726 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2727 if (!pIf)
2728 {
2729 Log(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
2730 return VERR_INVALID_HANDLE;
2731 }
2732
2733 /*
2734 * Grab the network semaphore and make the change.
2735 */
2736 int rc;
2737 PINTNETNETWORK pNetwork = pIf->pNetwork;
2738 if (pNetwork)
2739 {
2740 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2741 if (RT_SUCCESS(rc))
2742 {
2743 if (pIf->fPromiscuous != fPromiscuous)
2744 {
2745 Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
2746 hIf, !fPromiscuous, !!fPromiscuous));
2747 ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
2748 }
2749
2750 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2751 }
2752 }
2753 else
2754 rc = VERR_WRONG_ORDER;
2755
2756 intnetR0IfRelease(pIf, pSession);
2757 return rc;
2758}
2759
2760
2761/**
2762 * VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode.
2763 *
2764 * @returns see INTNETR0IfSetPromiscuousMode.
2765 * @param pIntNet The internal networking instance.
2766 * @param pSession The caller's session.
2767 * @param pReq The request packet.
2768 */
2769INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
2770{
2771 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2772 return VERR_INVALID_PARAMETER;
2773 return INTNETR0IfSetPromiscuousMode(pIntNet, pReq->hIf, pSession, pReq->fPromiscuous);
2774}
2775
2776
2777/**
2778 * Sets the MAC address of an interface.
2779 *
2780 * @returns VBox status code.
2781 * @param pIntNet The instance handle.
2782 * @param hIf The interface handle.
2783 * @param pSession The caller's session.
2784 * @param pMAC The new MAC address.
2785 */
2786INTNETR0DECL(int) INTNETR0IfSetMacAddress(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
2787{
2788 LogFlow(("INTNETR0IfSetMacAddress: pIntNet=%p hIf=%RX32 pMac=%p:{%.6Rhxs}\n", pIntNet, hIf, pMac, pMac));
2789
2790 /*
2791 * Validate & translate input.
2792 */
2793 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2794 AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
2795 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2796 if (!pIf)
2797 {
2798 Log(("INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
2799 return VERR_INVALID_HANDLE;
2800 }
2801
2802 /*
2803 * Grab the network semaphore and make the change.
2804 */
2805 int rc;
2806 PINTNETNETWORK pNetwork = pIf->pNetwork;
2807 if (pNetwork)
2808 {
2809 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2810 if (RT_SUCCESS(rc))
2811 {
2812 if (memcmp(&pIf->Mac, pMac, sizeof(pIf->Mac)))
2813 {
2814 Log(("INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
2815 hIf, &pIf->Mac, pMac));
2816 pIf->Mac = *pMac;
2817 pIf->fMacSet = true;
2818 }
2819
2820 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2821 }
2822 }
2823 else
2824 rc = VERR_WRONG_ORDER;
2825
2826 intnetR0IfRelease(pIf, pSession);
2827 return rc;
2828}
2829
2830
2831/**
2832 * VMMR0 request wrapper for INTNETR0IfSetMacAddress.
2833 *
2834 * @returns see INTNETR0IfSetMacAddress.
2835 * @param pIntNet The internal networking instance.
2836 * @param pSession The caller's session.
2837 * @param pReq The request packet.
2838 */
2839INTNETR0DECL(int) INTNETR0IfSetMacAddressReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
2840{
2841 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2842 return VERR_INVALID_PARAMETER;
2843 return INTNETR0IfSetMacAddress(pIntNet, pReq->hIf, pSession, &pReq->Mac);
2844}
2845
2846
2847/**
2848 * Worker for intnetR0IfSetActive.
2849 *
2850 * This function will update the active interface count on the network and
2851 * activate or deactivate the trunk connection if necessary. Note that in
2852 * order to do this it is necessary to abandond the network semaphore.
2853 *
2854 * @returns VBox status code.
2855 * @param pNetwork The network.
2856 * @param fIf The interface.
2857 * @param fActive What to do.
2858 */
2859static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
2860{
2861 /* quick santiy check */
2862 AssertPtr(pNetwork);
2863 AssertPtr(pIf);
2864
2865 /*
2866 * If we've got a trunk, lock it now in case we need to call out, and
2867 * then lock the network.
2868 */
2869 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2870 if (pTrunkIf && !intnetR0TrunkIfOutLock(pTrunkIf))
2871 return VERR_SEM_DESTROYED;
2872
2873 int rc = RTSemFastMutexRequest(pNetwork->FastMutex); AssertRC(rc);
2874 if (RT_SUCCESS(rc))
2875 {
2876 bool fNetworkLocked = true;
2877
2878 /*
2879 * Make the change if necessary.
2880 */
2881 if (pIf->fActive != fActive)
2882 {
2883 pIf->fActive = fActive;
2884
2885 uint32_t const cActiveIFs = pNetwork->cActiveIFs;
2886 Assert((int32_t)cActiveIFs + (fActive ? 1 : -1) >= 0);
2887 pNetwork->cActiveIFs += fActive ? 1 : -1;
2888
2889 if ( pTrunkIf
2890 && ( !pNetwork->cActiveIFs
2891 || !cActiveIFs))
2892 {
2893 /*
2894 * We'll have to change the trunk status, so, leave
2895 * the network semaphore so we don't create any deadlocks.
2896 */
2897 int rc2 = RTSemFastMutexRelease(pNetwork->FastMutex); AssertRC(rc2);
2898 fNetworkLocked = false;
2899
2900 if (pTrunkIf->pIfPort)
2901 pTrunkIf->pIfPort->pfnSetActive(pTrunkIf->pIfPort, fActive);
2902 }
2903 }
2904
2905 if (fNetworkLocked)
2906 RTSemFastMutexRelease(pNetwork->FastMutex);
2907 }
2908 if (pTrunkIf)
2909 intnetR0TrunkIfOutUnlock(pTrunkIf);
2910 return rc;
2911}
2912
2913
2914/**
2915 * Activates or deactivates a interface.
2916 *
2917 * This is used to enable and disable the trunk connection on demans as well as
2918 * know when not to expect an interface to want to receive packets.
2919 *
2920 * @returns VBox status code.
2921 * @param pIf The interface.
2922 * @param fActive What to do.
2923 */
2924static int intnetR0IfSetActive(PINTNETIF pIf, bool fActive)
2925{
2926 /* quick sanity check */
2927 AssertPtrReturn(pIf, VERR_INVALID_POINTER);
2928
2929 /*
2930 * Hand it to the network since it might involve the trunk
2931 * and things are tricky there wrt to locking order.
2932 */
2933 PINTNETNETWORK pNetwork = pIf->pNetwork;
2934 if (!pNetwork)
2935 return VERR_WRONG_ORDER;
2936 return intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2937}
2938
2939
2940/**
2941 * Sets the active property of an interface.
2942 *
2943 * @returns VBox status code.
2944 * @param pIntNet The instance handle.
2945 * @param hIf The interface handle.
2946 * @param pSession The caller's session.
2947 * @param fActive The new state.
2948 */
2949INTNETR0DECL(int) INTNETR0IfSetActive(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
2950{
2951 LogFlow(("INTNETR0IfSetActive: pIntNet=%p hIf=%RX32 fActive=%RTbool\n", pIntNet, hIf, fActive));
2952
2953 /*
2954 * Validate & translate input.
2955 */
2956 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2957 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2958 if (!pIf)
2959 {
2960 Log(("INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
2961 return VERR_INVALID_HANDLE;
2962 }
2963
2964 /*
2965 * Hand it to the network since it might involve the trunk
2966 * and things are tricky there wrt to locking order.
2967 */
2968 int rc;
2969 PINTNETNETWORK pNetwork = pIf->pNetwork;
2970 if (pNetwork)
2971 rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2972 else
2973 rc = VERR_WRONG_ORDER;
2974
2975 intnetR0IfRelease(pIf, pSession);
2976 return rc;
2977}
2978
2979
2980/**
2981 * VMMR0 request wrapper for INTNETR0IfSetActive.
2982 *
2983 * @returns see INTNETR0IfSetActive.
2984 * @param pIntNet The internal networking instance.
2985 * @param pSession The caller's session.
2986 * @param pReq The request packet.
2987 */
2988INTNETR0DECL(int) INTNETR0IfSetActiveReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
2989{
2990 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2991 return VERR_INVALID_PARAMETER;
2992 return INTNETR0IfSetActive(pIntNet, pReq->hIf, pSession, pReq->fActive);
2993}
2994
2995
2996/**
2997 * Wait for the interface to get signaled.
2998 * The interface will be signaled when is put into the receive buffer.
2999 *
3000 * @returns VBox status code.
3001 * @param pIntNet The instance handle.
3002 * @param hIf The interface handle.
3003 * @param pSession The caller's session.
3004 * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
3005 * used if indefinite wait is desired.
3006 */
3007INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
3008{
3009 Log4(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));
3010
3011 /*
3012 * Get and validate essential handles.
3013 */
3014 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3015 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3016 if (!pIf)
3017 {
3018 Log(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
3019 return VERR_INVALID_HANDLE;
3020 }
3021 const INTNETIFHANDLE hIfSelf = pIf->hIf;
3022 const RTSEMEVENT Event = pIf->Event;
3023 if ( hIfSelf != hIf /* paranoia */
3024 && Event != NIL_RTSEMEVENT)
3025 {
3026 Log(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
3027 return VERR_SEM_DESTROYED;
3028 }
3029
3030 /*
3031 * It is tempting to check if there is data to be read here,
3032 * but the problem with such an approach is that it will cause
3033 * one unnecessary supervisor->user->supervisor trip. There is
3034 * already a slight risk for such, so no need to increase it.
3035 */
3036
3037 /*
3038 * Increment the number of waiters before starting the wait.
3039 * Upon wakeup we must assert reality, checking that we're not
3040 * already destroyed or in the process of being destroyed. This
3041 * code must be aligned with the waiting code in intnetR0IfDestruct.
3042 */
3043 ASMAtomicIncU32(&pIf->cSleepers);
3044 int rc = RTSemEventWaitNoResume(Event, cMillies);
3045 if (pIf->Event == Event)
3046 {
3047 ASMAtomicDecU32(&pIf->cSleepers);
3048 if (!pIf->fDestroying)
3049 {
3050 if (intnetR0IfRelease(pIf, pSession))
3051 rc = VERR_SEM_DESTROYED;
3052 }
3053 else
3054 rc = VERR_SEM_DESTROYED;
3055 }
3056 else
3057 rc = VERR_SEM_DESTROYED;
3058 Log4(("INTNETR0IfWait: returns %Rrc\n", rc));
3059 return rc;
3060}
3061
3062
3063/**
3064 * VMMR0 request wrapper for INTNETR0IfWait.
3065 *
3066 * @returns see INTNETR0IfWait.
3067 * @param pIntNet The internal networking instance.
3068 * @param pSession The caller's session.
3069 * @param pReq The request packet.
3070 */
3071INTNETR0DECL(int) INTNETR0IfWaitReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
3072{
3073 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3074 return VERR_INVALID_PARAMETER;
3075 return INTNETR0IfWait(pIntNet, pReq->hIf, pSession, pReq->cMillies);
3076}
3077
3078
3079/**
3080 * Close an interface.
3081 *
3082 * @returns VBox status code.
3083 * @param pIntNet The instance handle.
3084 * @param hIf The interface handle.
3085 * @param pSession The caller's session.
3086 */
3087INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
3088{
3089 LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));
3090
3091 /*
3092 * Validate and free the handle.
3093 */
3094 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3095 PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
3096 if (!pIf)
3097 return VERR_INVALID_HANDLE;
3098
3099 /* mark the handle as freed so intnetR0IfDestruct won't free it again. */
3100 ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
3101
3102
3103 /*
3104 * Release the references to the interface object (handle + free lookup).
3105 * But signal the event semaphore first so any waiter holding a reference
3106 * will wake up too (he'll see hIf == invalid and return correctly).
3107 */
3108 RTSemEventSignal(pIf->Event);
3109
3110 void *pvObj = pIf->pvObj;
3111 intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
3112
3113 int rc = SUPR0ObjRelease(pvObj, pSession);
3114 LogFlow(("INTNETR0IfClose: returns %Rrc\n", rc));
3115 return rc;
3116}
3117
3118
3119/**
3120 * VMMR0 request wrapper for INTNETR0IfCloseReq.
3121 *
3122 * @returns see INTNETR0IfClose.
3123 * @param pIntNet The internal networking instance.
3124 * @param pSession The caller's session.
3125 * @param pReq The request packet.
3126 */
3127INTNETR0DECL(int) INTNETR0IfCloseReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
3128{
3129 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3130 return VERR_INVALID_PARAMETER;
3131 return INTNETR0IfClose(pIntNet, pReq->hIf, pSession);
3132}
3133
3134
3135/**
3136 * Interface destructor callback.
3137 * This is called for reference counted objectes when the count reaches 0.
3138 *
3139 * @param pvObj The object pointer.
3140 * @param pvUser1 Pointer to the interface.
3141 * @param pvUser2 Pointer to the INTNET instance data.
3142 */
3143static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3144{
3145 PINTNETIF pIf = (PINTNETIF)pvUser1;
3146 PINTNET pIntNet = (PINTNET)pvUser2;
3147 Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
3148
3149 RTSemFastMutexRequest(pIntNet->FastMutex);
3150
3151 /*
3152 * Mark the interface as being destroyed so the waiter
3153 * can behave appropriately (theoretical case).
3154 */
3155 ASMAtomicWriteBool(&pIf->fDestroying, true);
3156
3157 /*
3158 * Delete the interface handle so the object no longer can be used.
3159 * (Can happen if the client didn't close its session.)
3160 */
3161 INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
3162 if (hIf != INTNET_HANDLE_INVALID)
3163 {
3164 void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession); NOREF(pvObj2);
3165 AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
3166 }
3167
3168 /*
3169 * If we've got a network deactivate and unlink ourselves from it.
3170 * Because of cleanup order we might be an orphan now.
3171 */
3172 PINTNETNETWORK pNetwork = pIf->pNetwork;
3173 if (pNetwork)
3174 {
3175 intnetR0IfSetActive(pIf, false);
3176
3177 if (pNetwork->pIFs == pIf)
3178 pNetwork->pIFs = pIf->pNext;
3179 else
3180 {
3181 PINTNETIF pPrev = pNetwork->pIFs;
3182 while (pPrev)
3183 {
3184 if (pPrev->pNext == pIf)
3185 {
3186 pPrev->pNext = pIf->pNext;
3187 break;
3188 }
3189 pPrev = pPrev->pNext;
3190 }
3191 Assert(pPrev);
3192 }
3193 pIf->pNext = NULL;
3194
3195 /*
3196 * Release our reference to the network.
3197 */
3198 RTSemFastMutexRelease(pIntNet->FastMutex);
3199
3200 SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
3201 pIf->pNetwork = NULL;
3202 }
3203 else
3204 RTSemFastMutexRelease(pIntNet->FastMutex);
3205
3206 /*
3207 * Wakeup anyone waiting on this interface.
3208 *
3209 * We *must* make sure they have woken up properly and realized
3210 * that the interface is no longer valid.
3211 */
3212 if (pIf->Event != NIL_RTSEMEVENT)
3213 {
3214 RTSEMEVENT Event = pIf->Event;
3215 unsigned cMaxWait = 0x1000;
3216 while (pIf->cSleepers && cMaxWait-- > 0)
3217 {
3218 RTSemEventSignal(Event);
3219 RTThreadYield();
3220 }
3221 if (pIf->cSleepers)
3222 {
3223 RTThreadSleep(1);
3224
3225 cMaxWait = pIf->cSleepers;
3226 while (pIf->cSleepers && cMaxWait-- > 0)
3227 {
3228 RTSemEventSignal(Event);
3229 RTThreadSleep(10);
3230 }
3231 }
3232
3233 RTSemEventDestroy(Event);
3234 pIf->Event = NIL_RTSEMEVENT;
3235 }
3236
3237 /*
3238 * Unmap user buffer.
3239 */
3240 if (pIf->pIntBuf != pIf->pIntBufDefault)
3241 {
3242 /** @todo user buffer */
3243 }
3244
3245 /*
3246 * Unmap and Free the default buffer.
3247 */
3248 if (pIf->pIntBufDefault)
3249 {
3250 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
3251 pIf->pIntBufDefault = NULL;
3252 pIf->pIntBufDefaultR3 = 0;
3253 pIf->pIntBuf = NULL;
3254 pIf->pIntBufR3 = 0;
3255 }
3256
3257 /*
3258 * The interface.
3259 */
3260 pIf->pvObj = NULL;
3261 RTMemFree(pIf);
3262}
3263
3264
3265/**
3266 * Creates a new network interface.
3267 *
3268 * The call must have opened the network for the new interface
3269 * and is responsible for closing it on failure. On success
3270 * it must leave the network opened so the interface destructor
3271 * can close it.
3272 *
3273 * @returns VBox status code.
3274 * @param pNetwork The network.
3275 * @param pSession The session handle.
3276 * @param cbSend The size of the send buffer.
3277 * @param cbRecv The size of the receive buffer.
3278 * @param phIf Where to store the interface handle.
3279 */
3280static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, bool *pfCloseNetwork, PINTNETIFHANDLE phIf)
3281{
3282 LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
3283 pNetwork, pSession, cbSend, cbRecv, phIf));
3284
3285 /*
3286 * Assert input.
3287 */
3288 AssertPtr(pNetwork);
3289 AssertPtr(phIf);
3290 AssertPtr(pfCloseNetwork);
3291 *pfCloseNetwork = false;
3292
3293 /*
3294 * Allocate and initialize the interface structure.
3295 */
3296 PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
3297 if (!pIf)
3298 return VERR_NO_MEMORY;
3299 //pIf->pNext = NULL;
3300 memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
3301 //pIf->fMacSet = false;
3302 //pIf->fPromiscuous = false;
3303 //pIf->fActive = false;
3304 //pIf->fDestroying = false;
3305 //pIf->pIntBuf = 0;
3306 //pIf->pIntBufR3 = NIL_RTR3PTR;
3307 //pIf->pIntBufDefault = 0;
3308 //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
3309 //pIf->cYields = 0;
3310 pIf->Event = NIL_RTSEMEVENT;
3311 //pIf->cSleepers = 0;
3312 pIf->hIf = INTNET_HANDLE_INVALID;
3313 pIf->pNetwork = pNetwork;
3314 pIf->pSession = pSession;
3315 //pIf->pvObj = NULL;
3316 //pIf->aAddrCache[kIntNetAddrType_Invalid] = {0};
3317 //pIf->aAddrCache[kIntNetAddrType_IPv4].pbEntries = NULL;
3318 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntries = 0;
3319 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntriesAlloc = 0;
3320 pIf->aAddrCache[kIntNetAddrType_IPv4].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv4);
3321 pIf->aAddrCache[kIntNetAddrType_IPv4].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv4);
3322 //pIf->aAddrCache[kIntNetAddrType_IPv6].pbEntries = NULL;
3323 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntries = 0;
3324 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntriesAlloc = 0;
3325 pIf->aAddrCache[kIntNetAddrType_IPv6].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv6);
3326 pIf->aAddrCache[kIntNetAddrType_IPv6].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv6);
3327 //pIf->aAddrCache[kIntNetAddrType_IPX].pbEntries = NULL;
3328 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntries = 0;
3329 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntriesAlloc = 0;
3330 pIf->aAddrCache[kIntNetAddrType_IPX].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPX);
3331 pIf->aAddrCache[kIntNetAddrType_IPX].cbEntry = RT_ALIGN_32(intnetR0AddrSize(kIntNetAddrType_IPv4), 16);
3332 int rc = RTSemEventCreate((PRTSEMEVENT)&pIf->Event);
3333 if (RT_SUCCESS(rc))
3334 {
3335 /*
3336 * Create the default buffer.
3337 */
3338 /** @todo adjust with minimums and apply defaults here. */
3339 cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3340 cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3341 const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
3342 rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
3343 if (RT_SUCCESS(rc))
3344 {
3345 ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
3346
3347 pIf->pIntBuf = pIf->pIntBufDefault;
3348 pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
3349 pIf->pIntBuf->cbBuf = cbBuf;
3350 pIf->pIntBuf->cbRecv = cbRecv;
3351 pIf->pIntBuf->cbSend = cbSend;
3352 /* receive ring buffer. */
3353 pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
3354 pIf->pIntBuf->Recv.offRead = pIf->pIntBuf->Recv.offStart;
3355 pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
3356 pIf->pIntBuf->Recv.offEnd = pIf->pIntBuf->Recv.offStart + cbRecv;
3357 /* send ring buffer. */
3358 pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
3359 pIf->pIntBuf->Send.offRead = pIf->pIntBuf->Send.offStart;
3360 pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
3361 pIf->pIntBuf->Send.offEnd = pIf->pIntBuf->Send.offStart + cbSend;
3362
3363 /*
3364 * Link the interface to the network.
3365 */
3366 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3367 if (RT_SUCCESS(rc))
3368 {
3369 pIf->pNext = pNetwork->pIFs;
3370 pNetwork->pIFs = pIf;
3371 RTSemFastMutexRelease(pNetwork->FastMutex);
3372
3373 /*
3374 * Register the interface with the session.
3375 */
3376 pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, intnetR0IfDestruct, pIf, pNetwork->pIntNet);
3377 if (pIf->pvObj)
3378 {
3379 rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
3380 if (RT_SUCCESS(rc))
3381 {
3382 *phIf = pIf->hIf;
3383 Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
3384 *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
3385 return VINF_SUCCESS;
3386 }
3387
3388 SUPR0ObjRelease(pIf->pvObj, pSession);
3389 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3390 return rc;
3391 }
3392
3393 RTSemFastMutexDestroy(pNetwork->FastMutex);
3394 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3395 }
3396
3397 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
3398 pIf->pIntBufDefault = NULL;
3399 pIf->pIntBuf = NULL;
3400 }
3401
3402 RTSemEventDestroy(pIf->Event);
3403 pIf->Event = NIL_RTSEMEVENT;
3404 }
3405 RTMemFree(pIf);
3406 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3407 *pfCloseNetwork = true;
3408 return rc;
3409}
3410
3411
3412#ifdef RT_WITH_W64_UNWIND_HACK
3413# if defined(RT_OS_WINDOWS) && defined(RT_ARCH_AMD64)
3414# define INTNET_DECL_CALLBACK(type) DECLASM(DECLHIDDEN(type))
3415# define INTNET_CALLBACK(_n) intnetNtWrap##_n
3416
3417 /* wrapper callback declarations */
3418 INTNET_DECL_CALLBACK(bool) INTNET_CALLBACK(intnetR0TrunkIfPortSetSGPhys)(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable);
3419 INTNET_DECL_CALLBACK(bool) INTNET_CALLBACK(intnetR0TrunkIfPortRecv)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc);
3420 INTNET_DECL_CALLBACK(void) INTNET_CALLBACK(intnetR0TrunkIfPortSGRetain)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG);
3421 INTNET_DECL_CALLBACK(void) INTNET_CALLBACK(intnetR0TrunkIfPortSGRelease)(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG);
3422
3423# else
3424# error "UNSUPPORTED (RT_WITH_W64_UNWIND_HACK)"
3425# endif
3426#else
3427# define INTNET_DECL_CALLBACK(_t) static DECLCALLBACK(_t)
3428# define INTNET_CALLBACK(_n) _n
3429#endif
3430
3431/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
3432INTNET_DECL_CALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
3433{
3434 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3435 AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
3436 return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
3437}
3438
3439
3440/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
3441INTNET_DECL_CALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
3442{
3443 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3444 PINTNETNETWORK pNetwork = pThis->pNetwork;
3445
3446 /* assert some sanity */
3447 AssertPtrReturn(pNetwork, false);
3448 AssertReturn(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX, false);
3449 AssertPtr(pSG);
3450 Assert(fSrc);
3451
3452 /*
3453 * Lock the network and send the frame to it.
3454 */
3455 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3456 AssertRCReturn(rc, false);
3457
3458 bool fRc;
3459 if (RT_LIKELY(pNetwork->cActiveIFs > 0))
3460 fRc = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, false /* fTrunkLocked */);
3461 else
3462 fRc = false; /* don't drop it */
3463
3464 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
3465 AssertRC(rc);
3466
3467 return fRc;
3468}
3469
3470
3471/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
3472INTNET_DECL_CALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3473{
3474 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3475 PINTNETNETWORK pNetwork = pThis->pNetwork;
3476
3477 /* assert some sanity */
3478 AssertPtrReturnVoid(pNetwork);
3479 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3480 AssertPtr(pSG);
3481 Assert(pSG->cUsers > 0);
3482
3483 /* do it. */
3484 ++pSG->cUsers;
3485}
3486
3487
3488/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
3489INTNET_DECL_CALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3490{
3491 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3492 PINTNETNETWORK pNetwork = pThis->pNetwork;
3493
3494 /* assert some sanity */
3495 AssertPtrReturnVoid(pNetwork);
3496 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3497 AssertPtr(pSG);
3498 Assert(pSG->cUsers > 0);
3499
3500 /*
3501 * Free it?
3502 */
3503 if (!--pSG->cUsers)
3504 {
3505 /** @todo later */
3506 }
3507}
3508
3509
3510/**
3511 * Retain the trunk interface.
3512 *
3513 * @returns pThis if retained.
3514 *
3515 * @param pThis The trunk.
3516 *
3517 * @remarks Any locks.
3518 */
3519static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
3520{
3521 if (pThis && pThis->pIfPort)
3522 {
3523 pThis->pIfPort->pfnRetain(pThis->pIfPort);
3524 return pThis;
3525 }
3526 return NULL;
3527}
3528
3529
3530/**
3531 * Release the trunk interface.
3532 *
3533 * @param pThis The trunk.
3534 */
3535static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
3536{
3537 if (pThis && pThis->pIfPort)
3538 pThis->pIfPort->pfnRelease(pThis->pIfPort);
3539}
3540
3541
3542/**
3543 * Takes the out-bound trunk lock.
3544 *
3545 * This will ensure that pIfPort is valid.
3546 *
3547 * @returns success indicator.
3548 * @param pThis The trunk.
3549 *
3550 * @remarks No locks other than the create/destroy one.
3551 */
3552static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis)
3553{
3554 AssertPtrReturn(pThis, false);
3555 int rc = RTSemFastMutexRequest(pThis->FastMutex);
3556 if (RT_SUCCESS(rc))
3557 {
3558 if (RT_LIKELY(pThis->pIfPort))
3559 return true;
3560 RTSemFastMutexRelease(pThis->FastMutex);
3561 }
3562 else
3563 AssertMsg(rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc));
3564 return false;
3565}
3566
3567
3568/**
3569 * Releases the out-bound trunk lock.
3570 *
3571 * @param pThis The trunk.
3572 */
3573static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis)
3574{
3575 if (pThis)
3576 {
3577 int rc = RTSemFastMutexRelease(pThis->FastMutex);
3578 AssertRC(rc);
3579 }
3580}
3581
3582
3583/**
3584 * Activates the trunk interface.
3585 *
3586 * @param pThis The trunk.
3587 * @param fActive What to do with it.
3588 *
3589 * @remarks Caller may only own the create/destroy lock.
3590 */
3591static void intnetR0TrunkIfActivate(PINTNETTRUNKIF pThis, bool fActive)
3592{
3593 if (intnetR0TrunkIfOutLock(pThis))
3594 {
3595 pThis->pIfPort->pfnSetActive(pThis->pIfPort, fActive);
3596 intnetR0TrunkIfOutUnlock(pThis);
3597 }
3598}
3599
3600
3601/**
3602 * Shutdown the trunk interface.
3603 *
3604 * @param pThis The trunk.
3605 * @param pNetworks The network.
3606 *
3607 * @remarks The caller must *NOT* hold the network lock. The global
3608 * create/destroy lock is fine though.
3609 */
3610static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
3611{
3612 /* assert sanity */
3613 if (!pThis)
3614 return;
3615 AssertPtr(pThis);
3616 Assert(pThis->pNetwork == pNetwork);
3617 AssertPtrNull(pThis->pIfPort);
3618
3619 /*
3620 * The interface has already been deactivated, we just to wait for
3621 * it to become idle before we can disconnect and release it.
3622 */
3623 PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
3624 if (pIfPort)
3625 {
3626 intnetR0TrunkIfOutLock(pThis);
3627
3628 /* unset it */
3629 pThis->pIfPort = NULL;
3630
3631 /* wait in portions so we can complain ever now an then. */
3632 uint64_t StartTS = RTTimeSystemNanoTS();
3633 int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3634 if (RT_FAILURE(rc))
3635 {
3636 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3637 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3638 Assert(rc == VERR_TIMEOUT);
3639 while ( RT_FAILURE(rc)
3640 && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
3641 rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3642 if (rc == VERR_TIMEOUT)
3643 {
3644 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3645 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3646 while ( rc == VERR_TIMEOUT
3647 && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
3648 rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
3649 if (RT_FAILURE(rc))
3650 {
3651 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
3652 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3653 AssertRC(rc);
3654 }
3655 }
3656 }
3657
3658 /* disconnect & release it. */
3659 pIfPort->pfnDisconnectAndRelease(pIfPort);
3660 }
3661
3662 /*
3663 * Free up the resources.
3664 */
3665 RTSEMFASTMUTEX FastMutex = pThis->FastMutex;
3666 pThis->FastMutex = NIL_RTSEMFASTMUTEX;
3667 pThis->pNetwork = NULL;
3668 RTSemFastMutexRelease(FastMutex);
3669 RTSemFastMutexDestroy(FastMutex);
3670 RTMemFree(pThis);
3671}
3672
3673
3674/**
3675 * Creates the trunk connection (if any).
3676 *
3677 * @returns VBox status code.
3678 *
3679 * @param pNetwork The newly created network.
3680 * @param pSession The session handle.
3681 */
3682static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3683{
3684 const char *pszName;
3685 switch (pNetwork->enmTrunkType)
3686 {
3687 /*
3688 * The 'None' case, simple.
3689 */
3690 case kIntNetTrunkType_None:
3691 case kIntNetTrunkType_WhateverNone:
3692 return VINF_SUCCESS;
3693
3694 /* Can't happen, but makes GCC happy. */
3695 default:
3696 return VERR_NOT_IMPLEMENTED;
3697
3698 /*
3699 * Translate enum to component factory name.
3700 */
3701 case kIntNetTrunkType_NetFlt:
3702 pszName = "VBoxNetFlt";
3703 break;
3704 case kIntNetTrunkType_NetTap:
3705 pszName = "VBoxNetTap";
3706 break;
3707 case kIntNetTrunkType_SrvNat:
3708 pszName = "VBoxSrvNat";
3709 break;
3710 }
3711
3712 /*
3713 * Allocate the trunk interface.
3714 */
3715 PINTNETTRUNKIF pTrunkIF = (PINTNETTRUNKIF)RTMemAllocZ(sizeof(*pTrunkIF));
3716 if (!pTrunkIF)
3717 return VERR_NO_MEMORY;
3718 pTrunkIF->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
3719 pTrunkIF->SwitchPort.pfnSetSGPhys = INTNET_CALLBACK(intnetR0TrunkIfPortSetSGPhys);
3720 pTrunkIF->SwitchPort.pfnRecv = INTNET_CALLBACK(intnetR0TrunkIfPortRecv);
3721 pTrunkIF->SwitchPort.pfnSGRetain = INTNET_CALLBACK(intnetR0TrunkIfPortSGRetain);
3722 pTrunkIF->SwitchPort.pfnSGRelease = INTNET_CALLBACK(intnetR0TrunkIfPortSGRelease);
3723 pTrunkIF->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
3724 //pTrunkIF->pIfPort = NULL;
3725 pTrunkIF->pNetwork = pNetwork;
3726 //pTrunkIF->fPhysSG = false;
3727 //pTrunkIF->fPromiscuousWire = false;
3728 pTrunkIF->CachedMac.au8[0] = 0xfe;
3729 pTrunkIF->CachedMac.au8[1] = 0xff;
3730 pTrunkIF->CachedMac.au8[2] = 0xff;
3731 pTrunkIF->CachedMac.au8[3] = 0xff;
3732 pTrunkIF->CachedMac.au8[4] = 0xff;
3733 pTrunkIF->CachedMac.au8[5] = 0xff;
3734 int rc = RTSemFastMutexCreate(&pTrunkIF->FastMutex);
3735 if (RT_SUCCESS(rc))
3736 {
3737#ifdef IN_RING0 /* (testcase is ring-3) */
3738 /*
3739 * Query the factory we want, then use it create and connect the trunk.
3740 */
3741 PINTNETTRUNKFACTORY pTrunkFactory = NULL;
3742 rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
3743 if (RT_SUCCESS(rc))
3744 {
3745 rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory, pNetwork->szTrunk, &pTrunkIF->SwitchPort, &pTrunkIF->pIfPort, !!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
3746 pTrunkFactory->pfnRelease(pTrunkFactory);
3747 if (RT_SUCCESS(rc))
3748 {
3749 Assert(pTrunkIF->pIfPort);
3750 pNetwork->pTrunkIF = pTrunkIF;
3751 Log(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
3752 pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
3753 return VINF_SUCCESS;
3754 }
3755 }
3756#endif /* IN_RING0 */
3757 RTSemFastMutexDestroy(pTrunkIF->FastMutex);
3758 }
3759 RTMemFree(pTrunkIF);
3760 LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
3761 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
3762 return rc;
3763}
3764
3765
3766
3767/**
3768 * Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork().
3769 *
3770 * @param pNetwork The network to close.
3771 * @param pSession The session handle.
3772 */
3773static int intnetR0NetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3774{
3775 LogFlow(("intnetR0NetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
3776 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
3777 AssertPtrReturn(pNetwork, VERR_INVALID_PARAMETER);
3778
3779 int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
3780 LogFlow(("intnetR0NetworkClose: return %Rrc\n", rc));
3781 return rc;
3782}
3783
3784
3785/**
3786 * Object destructor callback.
3787 * This is called for reference counted objectes when the count reaches 0.
3788 *
3789 * @param pvObj The object pointer.
3790 * @param pvUser1 Pointer to the network.
3791 * @param pvUser2 Pointer to the INTNET instance data.
3792 */
3793static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3794{
3795 PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
3796 PINTNET pIntNet = (PINTNET)pvUser2;
3797 Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
3798 Assert(pNetwork->pIntNet == pIntNet);
3799
3800 /* take the create/destroy sem. */
3801 RTSemFastMutexRequest(pIntNet->FastMutex);
3802
3803 /*
3804 * Deactivate the trunk connection first (if any).
3805 */
3806 if (pNetwork->pTrunkIF)
3807 intnetR0TrunkIfActivate(pNetwork->pTrunkIF, false /* fActive */);
3808
3809 /*
3810 * Unlink the network.
3811 * Note that it needn't be in the list if we failed during creation.
3812 */
3813 PINTNETNETWORK pPrev = pIntNet->pNetworks;
3814 if (pPrev == pNetwork)
3815 pIntNet->pNetworks = pNetwork->pNext;
3816 else
3817 {
3818 for (; pPrev; pPrev = pPrev->pNext)
3819 if (pPrev->pNext == pNetwork)
3820 {
3821 pPrev->pNext = pNetwork->pNext;
3822 break;
3823 }
3824 }
3825 pNetwork->pNext = NULL;
3826 pNetwork->pvObj = NULL;
3827
3828 /*
3829 * Because of the undefined order of the per session object dereferencing when closing a session,
3830 * we have to handle the case where the network is destroyed before the interfaces. We'll
3831 * deal with this by simply orphaning the interfaces.
3832 */
3833 RTSemFastMutexRequest(pNetwork->FastMutex);
3834
3835 PINTNETIF pCur = pNetwork->pIFs;
3836 while (pCur)
3837 {
3838 PINTNETIF pNext = pCur->pNext;
3839 pCur->pNext = NULL;
3840 pCur->pNetwork = NULL;
3841 pCur = pNext;
3842 }
3843
3844 /* Grab and zap the trunk pointer before leaving the mutex. */
3845 PINTNETTRUNKIF pTrunkIF = pNetwork->pTrunkIF;
3846 pNetwork->pTrunkIF = NULL;
3847
3848 RTSemFastMutexRelease(pNetwork->FastMutex);
3849
3850 /*
3851 * If there is a trunk, delete it.
3852 * Note that this may tak a while if we're unlucky...
3853 */
3854 if (pTrunkIF)
3855 intnetR0TrunkIfDestroy(pTrunkIF, pNetwork);
3856
3857 /*
3858 * Free resources.
3859 */
3860 RTSemFastMutexDestroy(pNetwork->FastMutex);
3861 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3862 RTMemFree(pNetwork);
3863
3864 /* release the create/destroy sem. (can be done before trunk destruction.) */
3865 RTSemFastMutexRelease(pIntNet->FastMutex);
3866}
3867
3868
3869/**
3870 * Opens an existing network.
3871 *
3872 * @returns VBox status code.
3873 * @param pIntNet The instance data.
3874 * @param pSession The current session.
3875 * @param pszNetwork The network name. This has a valid length.
3876 * @param enmTrunkType The trunk type.
3877 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3878 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3879 * @param ppNetwork Where to store the pointer to the network on success.
3880 */
3881static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3882 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3883{
3884 LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3885 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3886
3887 /* just pro forma validation, the caller is internal. */
3888 AssertPtr(pIntNet);
3889 AssertPtr(pSession);
3890 AssertPtr(pszNetwork);
3891 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3892 AssertPtr(pszTrunk);
3893 Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
3894 AssertPtr(ppNetwork);
3895 *ppNetwork = NULL;
3896
3897 /*
3898 * Search networks by name.
3899 */
3900 PINTNETNETWORK pCur;
3901 uint8_t cchName = strlen(pszNetwork);
3902 Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
3903
3904 pCur = pIntNet->pNetworks;
3905 while (pCur)
3906 {
3907 if ( pCur->cchName == cchName
3908 && !memcmp(pCur->szName, pszNetwork, cchName))
3909 {
3910 /*
3911 * Found the network, now check that we have the same ideas
3912 * about the trunk setup and security.
3913 */
3914 int rc;
3915 if ( enmTrunkType == kIntNetTrunkType_WhateverNone
3916 || ( pCur->enmTrunkType == enmTrunkType
3917 && !strcmp(pCur->szTrunk, pszTrunk)))
3918 {
3919 if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
3920 {
3921
3922 /*
3923 * Increment the reference and check that the session
3924 * can access this network.
3925 */
3926 rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
3927 if (RT_SUCCESS(rc))
3928 {
3929 if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
3930 rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
3931 if (RT_SUCCESS(rc))
3932 {
3933 pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
3934
3935 *ppNetwork = pCur;
3936 }
3937 else
3938 SUPR0ObjRelease(pCur->pvObj, pSession);
3939 }
3940 else if (rc == VERR_WRONG_ORDER)
3941 rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
3942 }
3943 else
3944 rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
3945 }
3946 else
3947 rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
3948
3949 LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
3950 return rc;
3951 }
3952 pCur = pCur->pNext;
3953 }
3954
3955 LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
3956 return VERR_NOT_FOUND;
3957}
3958
3959
3960/**
3961 * Creates a new network.
3962 *
3963 * The call must own the INTNET::FastMutex and has already attempted
3964 * opening the network and found it to be non-existing.
3965 *
3966 * @returns VBox status code.
3967 * @param pIntNet The instance data.
3968 * @param pSession The session handle.
3969 * @param pszNetwork The name of the network. This must be at least one character long and no longer
3970 * than the INTNETNETWORK::szName.
3971 * @param enmTrunkType The trunk type.
3972 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3973 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3974 * @param ppNetwork Where to store the network. In the case of failure whatever is returned
3975 * here should be dereferenced outside the INTNET::FastMutex.
3976 */
3977static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3978 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3979{
3980 LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3981 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3982
3983 /* just pro forma validation, the caller is internal. */
3984 AssertPtr(pIntNet);
3985 AssertPtr(pSession);
3986 AssertPtr(pszNetwork);
3987 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3988 AssertPtr(pszTrunk);
3989 Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
3990 AssertPtr(ppNetwork);
3991 *ppNetwork = NULL;
3992
3993 /*
3994 * Allocate and initialize.
3995 */
3996 size_t cb = sizeof(INTNETNETWORK);
3997 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3998 cb += INTNETNETWORK_TMP_SIZE + 64;
3999 PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(cb);
4000 if (!pNew)
4001 return VERR_NO_MEMORY;
4002 int rc = RTSemFastMutexCreate(&pNew->FastMutex);
4003 if (RT_SUCCESS(rc))
4004 {
4005 //pNew->pIFs = NULL;
4006 pNew->pIntNet = pIntNet;
4007 //pNew->cActiveIFs = 0;
4008 pNew->fFlags = fFlags;
4009 size_t cchName = strlen(pszNetwork);
4010 pNew->cchName = cchName;
4011 Assert(cchName && cchName < sizeof(pNew->szName)); /* caller's responsibility. */
4012 memcpy(pNew->szName, pszNetwork, cchName); /* '\0' by alloc. */
4013 pNew->enmTrunkType = enmTrunkType;
4014 Assert(strlen(pszTrunk) < sizeof(pNew->szTrunk)); /* caller's responsibility. */
4015 strcpy(pNew->szTrunk, pszTrunk);
4016 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
4017 pNew->pbTmp = RT_ALIGN_PT(pNew + 1, 64, uint8_t *);
4018 //else
4019 // pNew->pbTmp = NULL;
4020
4021 /*
4022 * Register the object in the current session and link it into the network list.
4023 */
4024 pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNew, pIntNet);
4025 if (pNew->pvObj)
4026 {
4027 pNew->pNext = pIntNet->pNetworks;
4028 pIntNet->pNetworks = pNew;
4029
4030 /*
4031 * Check if the current session is actually allowed to create and open
4032 * the network. It is possible to implement network name based policies
4033 * and these must be checked now. SUPR0ObjRegister does no such checks.
4034 */
4035 rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
4036 if (RT_SUCCESS(rc))
4037 {
4038 /*
4039 * Connect the trunk.
4040 */
4041 rc = intnetR0NetworkCreateTrunkIf(pNew, pSession);
4042 if (RT_SUCCESS(rc))
4043 {
4044 *ppNetwork = pNew;
4045 LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
4046 return VINF_SUCCESS;
4047 }
4048 }
4049
4050 /*
4051 * We unlink it here so it cannot be opened when the caller leaves
4052 * INTNET::FastMutex before dereferencing it.
4053 */
4054 Assert(pIntNet->pNetworks == pNew);
4055 pIntNet->pNetworks = pNew->pNext;
4056 pNew->pNext = NULL;
4057
4058 *ppNetwork = pNew;
4059 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
4060 return rc;
4061 }
4062 rc = VERR_NO_MEMORY;
4063
4064 RTSemFastMutexDestroy(pNew->FastMutex);
4065 pNew->FastMutex = NIL_RTSEMFASTMUTEX;
4066 }
4067 RTMemFree(pNew);
4068 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
4069 return rc;
4070}
4071
4072
4073/**
4074 * Opens a network interface and connects it to the specified network.
4075 *
4076 * @returns VBox status code.
4077 * @param pIntNet The internal network instance.
4078 * @param pSession The session handle.
4079 * @param pszNetwork The network name.
4080 * @param enmTrunkType The trunk type.
4081 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
4082 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
4083 * @param fRestrictAccess Whether new participants should be subjected to access check or not.
4084 * @param cbSend The send buffer size.
4085 * @param cbRecv The receive buffer size.
4086 * @param phIf Where to store the handle to the network interface.
4087 */
4088INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork,
4089 INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
4090 unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
4091{
4092 LogFlow(("INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
4093 pIntNet, pSession, pszNetwork, pszNetwork, pszTrunk, pszTrunk, enmTrunkType, fFlags, cbSend, cbRecv, phIf));
4094
4095 /*
4096 * Validate input.
4097 */
4098 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
4099
4100 AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
4101 const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
4102 AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
4103 size_t cchNetwork = pszNetworkEnd - pszNetwork;
4104 AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
4105
4106 if (pszTrunk)
4107 {
4108 AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
4109 const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
4110 AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
4111 }
4112 else
4113 pszTrunk = "";
4114
4115 AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
4116 ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
4117 switch (enmTrunkType)
4118 {
4119 case kIntNetTrunkType_None:
4120 case kIntNetTrunkType_WhateverNone:
4121 AssertReturn(!*pszTrunk, VERR_INVALID_PARAMETER);
4122 break;
4123
4124 case kIntNetTrunkType_NetFlt:
4125 AssertReturn(pszTrunk, VERR_INVALID_PARAMETER);
4126 break;
4127
4128 default:
4129 return VERR_NOT_IMPLEMENTED;
4130 }
4131
4132 AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
4133 AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
4134
4135 /*
4136 * Acquire the mutex to serialize open/create.
4137 */
4138 int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
4139 if (RT_FAILURE(rc))
4140 return rc;
4141
4142 /*
4143 * Try open / create the network and create an interface on it for the caller to use.
4144 *
4145 * Note that because of the destructors grabbing INTNET::FastMutex and us being required
4146 * to own this semaphore for the entire network opening / creation and interface creation
4147 * sequence, intnetR0CreateNetwork will have to defer the network cleanup to us on failure.
4148 */
4149 PINTNETNETWORK pNetwork = NULL;
4150 rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
4151 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
4152 {
4153 bool fCloseNetwork = true;
4154 if (rc == VERR_NOT_FOUND)
4155 rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
4156 if (RT_SUCCESS(rc))
4157 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, &fCloseNetwork, phIf);
4158
4159 RTSemFastMutexRelease(pIntNet->FastMutex);
4160
4161 if (RT_FAILURE(rc) && pNetwork && fCloseNetwork)
4162 intnetR0NetworkClose(pNetwork, pSession);
4163 }
4164 else
4165 RTSemFastMutexRelease(pIntNet->FastMutex);
4166
4167 LogFlow(("INTNETR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
4168 return rc;
4169}
4170
4171
4172/**
4173 * VMMR0 request wrapper for GMMR0MapUnmapChunk.
4174 *
4175 * @returns see GMMR0MapUnmapChunk.
4176 * @param pIntNet The internal networking instance.
4177 * @param pSession The caller's session.
4178 * @param pReq The request packet.
4179 */
4180INTNETR0DECL(int) INTNETR0OpenReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
4181{
4182 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
4183 return VERR_INVALID_PARAMETER;
4184 return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
4185 pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
4186}
4187
4188
4189/**
4190 * Destroys an instance of the Ring-0 internal networking service.
4191 *
4192 * @param pIntNet Pointer to the instance data.
4193 */
4194INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
4195{
4196 LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));
4197
4198 /*
4199 * Allow NULL pointers.
4200 */
4201 if (!pIntNet)
4202 return;
4203 AssertPtrReturnVoid(pIntNet);
4204
4205 /*
4206 * There is not supposed to be any networks hanging around at this time.
4207 */
4208 Assert(pIntNet->pNetworks == NULL);
4209 if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
4210 {
4211 RTSemFastMutexDestroy(pIntNet->FastMutex);
4212 pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
4213 }
4214 if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
4215 {
4216 /** @todo does it make sense to have a deleter here? */
4217 RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
4218 pIntNet->hHtIfs = NIL_RTHANDLETABLE;
4219 }
4220
4221 RTMemFree(pIntNet);
4222}
4223
4224
4225/**
4226 * Create an instance of the Ring-0 internal networking service.
4227 *
4228 * @returns VBox status code.
4229 * @param ppIntNet Where to store the instance pointer.
4230 */
4231INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
4232{
4233 LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
4234 int rc = VERR_NO_MEMORY;
4235 PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
4236 if (pIntNet)
4237 {
4238 //pIntNet->pNetworks = NULL;
4239
4240 rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
4241 if (RT_SUCCESS(rc))
4242 {
4243 rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
4244 UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
4245 if (RT_SUCCESS(rc))
4246 {
4247 *ppIntNet = pIntNet;
4248 LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
4249 return VINF_SUCCESS;
4250 }
4251
4252 RTSemFastMutexDestroy(pIntNet->FastMutex);
4253 }
4254 RTMemFree(pIntNet);
4255 }
4256 *ppIntNet = NULL;
4257 LogFlow(("INTNETR0Create: returns %Rrc\n", rc));
4258 return rc;
4259}
4260
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