VirtualBox

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

Last change on this file since 15793 was 15757, checked in by vboxsync, 16 years ago

SrvIntNetR0: fix to not refer the freed memory

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