VirtualBox

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

Last change on this file since 15222 was 14695, checked in by vboxsync, 16 years ago

IntNet: Quick hack to force broadcast flag in DHCP requests on shared MAC.

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

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