VirtualBox

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

Last change on this file since 28275 was 28208, checked in by vboxsync, 15 years ago

intnet,VBoxNetFlt-linux: Added INTNETTRUNKSWPORT::pfnPreRecv for doing filtering and switching in difficult contexts. It currently returns broadcast all the time, but that will be addressed before long.

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