VirtualBox

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

Last change on this file since 10978 was 10978, checked in by vboxsync, 17 years ago

tstIntNet-1,iprt: Use iprt/net in tstIntNet-1. Modified the RTNETETHERHDR struct a bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.4 KB
Line 
1/* $Id: SrvIntNetR0.cpp 10978 2008-07-30 12:34:20Z vboxsync $ */
2/** @file
3 * Internal networking - The ring 0 service.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_SRV_INTNET
27#include <VBox/intnet.h>
28#include <VBox/sup.h>
29#include <VBox/pdm.h>
30#include <VBox/log.h>
31#include <iprt/asm.h>
32#include <iprt/alloc.h>
33#include <iprt/semaphore.h>
34#include <iprt/spinlock.h>
35#include <iprt/thread.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include <iprt/time.h>
39#include <iprt/handletable.h>
40#include <iprt/net.h>
41
42
43/*******************************************************************************
44* Structures and Typedefs *
45*******************************************************************************/
46typedef enum INTNETADDRTYPE
47{
48 /** The invalid 0 entry. */
49 kIntNetAddrType_Invalid = 0,
50 /** IP version 4. */
51 kIntNetAddrType_IPv4,
52 /** IP version 6. */
53 kIntNetAddrType_IPv6,
54 /** IPX. */
55 kIntNetAddrType_IPX,
56 /** The end of the valid values. */
57 kIntNetAddrType_End,
58 /** The usual 32-bit hack. */
59 kIntNetAddrType_32BitHack = 0x7fffffff
60} INTNETADDRTYPE;
61/** Pointer to a network layer address type. */
62typedef INTNETADDRTYPE *PINTNETADDRTYPE;
63
64
65/**
66 * Address and type.
67 */
68typedef struct INTNETADDR
69{
70 /** The address type. */
71 INTNETADDRTYPE enmType;
72 /** The address. */
73 RTNETADDRU Addr;
74} INTNETADDR;
75/** Pointer to an address. */
76typedef INTNETADDR *PINTNETADDR;
77/** Pointer to a const address. */
78typedef INTNETADDR const *PCINTNETADDR;
79
80
81/**
82 * Address cache for a specific network layer.
83 */
84typedef struct INTNETADDRCACHE
85{
86 /** Pointer to the table of addresses. */
87 uint8_t *pbEntries;
88 /** The number of valid address entries. */
89 uint8_t cEntries;
90 /** The number of allocated address entries. */
91 uint8_t cEntriesAlloc;
92 /** The address size. */
93 uint8_t cbAddress;
94 /** The size of an entry. */
95 uint8_t cbEntry;
96} INTNETADDRCACHE;
97/** Pointer to an address cache. */
98typedef INTNETADDRCACHE *PINTNETADDRCACHE;
99/** Pointer to a const address cache. */
100typedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
101
102
103/**
104 * A network interface.
105 *
106 * Unless explicitly stated, all members are protect by the network semaphore.
107 */
108typedef struct INTNETIF
109{
110 /** Pointer to the next interface.
111 * This is protected by the INTNET::FastMutex. */
112 struct INTNETIF *pNext;
113 /** The current MAC address for the interface. */
114 PDMMAC Mac;
115 /** Set if the INTNET::Mac member is valid. */
116 bool fMacSet;
117 /** Set if the interface is in promiscuous mode.
118 * In promiscuous mode the interface will receive all packages except the one it's sending. */
119 bool fPromiscuous;
120 /** Whether the interface is active or not. */
121 bool fActive;
122 /** Whether someone is currently in the destructor. */
123 bool volatile fDestroying;
124 /** Number of yields done to try make the interface read pending data.
125 * We will stop yeilding when this reaches a threshold assuming that the VM is paused or
126 * that it simply isn't worth all the delay. It is cleared when a successful send has been done.
127 */
128 uint32_t cYields;
129 /** Pointer to the current exchange buffer (ring-0). */
130 PINTNETBUF pIntBuf;
131 /** Pointer to ring-3 mapping of the current exchange buffer. */
132 R3PTRTYPE(PINTNETBUF) pIntBufR3;
133 /** Pointer to the default exchange buffer for the interface. */
134 PINTNETBUF pIntBufDefault;
135 /** Pointer to ring-3 mapping of the default exchange buffer. */
136 R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
137 /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
138 RTSEMEVENT volatile Event;
139 /** Number of threads sleeping on the Event semaphore. */
140 uint32_t cSleepers;
141 /** The interface handle.
142 * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
143 * should return with the appropriate error condition. */
144 INTNETIFHANDLE volatile hIf;
145 /** Pointer to the network this interface is connected to.
146 * This is protected by the INTNET::FastMutex. */
147 struct INTNETNETWORK *pNetwork;
148 /** The session this interface is associated with. */
149 PSUPDRVSESSION pSession;
150 /** The SUPR0 object id. */
151 void *pvObj;
152 /** The network layer address cache. (Indexed by type, 0 entry isn't used.) */
153 INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
154} INTNETIF;
155/** Pointer to an internal network interface. */
156typedef INTNETIF *PINTNETIF;
157
158
159/**
160 * A trunk interface.
161 */
162typedef struct INTNETTRUNKIF
163{
164 /** The port interface we present to the component. */
165 INTNETTRUNKSWPORT SwitchPort;
166 /** The port interface we get from the component. */
167 PINTNETTRUNKIFPORT pIfPort;
168 /** The trunk mutex that serializes all calls <b>to</b> the component. */
169 RTSEMFASTMUTEX FastMutex;
170 /** Pointer to the network we're connect to.
171 * This may be NULL if we're orphaned? */
172 struct INTNETNETWORK *pNetwork;
173 /** Whether to supply physical addresses with the outbound SGs. */
174 bool volatile fPhysSG;
175 /** Set if the 'wire' is in promiscuous mode.
176 * The state of the 'host' is queried each time. */
177 bool fPromiscuousWire;
178} INTNETTRUNKIF;
179/** Pointer to a trunk interface. */
180typedef INTNETTRUNKIF *PINTNETTRUNKIF;
181
182/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
183#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
184
185
186/**
187 * Internal representation of a network.
188 */
189typedef struct INTNETNETWORK
190{
191 /** The Next network in the chain.
192 * This is protected by the INTNET::FastMutex. */
193 struct INTNETNETWORK *pNext;
194 /** List of interfaces connected to the network.
195 * This is protected by the INTNET::FastMutex. */
196 PINTNETIF pIFs;
197 /** Pointer to the trunk interface.
198 * Can be NULL if there is no trunk connection. */
199 PINTNETTRUNKIF pTrunkIF;
200 /** The network mutex.
201 * It protects everything dealing with this network. */
202 RTSEMFASTMUTEX FastMutex;
203 /** Pointer to the instance data. */
204 struct INTNET *pIntNet;
205 /** The SUPR0 object id. */
206 void *pvObj;
207 /** Network creation flags (INTNET_OPEN_FLAGS_*). */
208 uint32_t fFlags;
209 /** The number of active interfaces (excluding the trunk). */
210 uint32_t cActiveIFs;
211 /** The length of the network name. */
212 uint8_t cchName;
213 /** The network name. */
214 char szName[INTNET_MAX_NETWORK_NAME];
215 /** The trunk type. */
216 INTNETTRUNKTYPE enmTrunkType;
217 /** The trunk name. */
218 char szTrunk[INTNET_MAX_TRUNK_NAME];
219} INTNETNETWORK;
220/** Pointer to an internal network. */
221typedef INTNETNETWORK *PINTNETNETWORK;
222
223
224/**
225 * Internal networking instance.
226 */
227typedef struct INTNET
228{
229 /** Mutex protecting the network creation, opening and destruction.
230 * (This means all operations affecting the pNetworks list.) */
231 RTSEMFASTMUTEX FastMutex;
232 /** List of networks. Protected by INTNET::Spinlock. */
233 PINTNETNETWORK volatile pNetworks;
234 /** Handle table for the interfaces. */
235 RTHANDLETABLE hHtIfs;
236} INTNET;
237
238
239#pragma pack(1)
240/**
241 * Ethernet IPv6 + 6-byte MAC ARP request packet.
242 *
243 * @todo check if this exists and is actually used.
244 */
245typedef struct INTNETARPIPV6
246{
247 RTNETARPHDR Hdr;
248 /** The sender hardware address. */
249 PDMMAC ar_sha;
250 /** The sender protocol address. */
251 RTNETADDRIPV6 ar_spa;
252 /** The target hardware address. */
253 PDMMAC ar_tha;
254 /** The arget protocol address. */
255 RTNETADDRIPV6 ar_tpa;
256} INTNETARPIPV6;
257#pragma pack(0)
258/** Pointer to an ethernet IPv6+MAC ARP request packet. */
259typedef INTNETARPIPV6 *PINTNETARPIPV6;
260/** Pointer to a const ethernet IPv6+MAC ARP request packet. */
261typedef INTNETARPIPV6 const *PCINTNETARPIPV6;
262
263
264/*******************************************************************************
265* Internal Functions *
266*******************************************************************************/
267static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
268static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
269static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis);
270static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis);
271
272
273
274/**
275 * Retain an interface.
276 *
277 * @returns VBox status code, can assume success in most situations.
278 * @param pIf The interface instance.
279 * @param pSession The current session.
280 */
281DECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
282{
283 int rc = SUPR0ObjAddRef(pIf->pvObj, pSession);
284 AssertRCReturn(rc, rc);
285 return VINF_SUCCESS;
286}
287
288
289/**
290 * Release an interface previously retained by intnetR0IfRetain or
291 * by handle lookup/freeing.
292 *
293 * @returns VBox status code, can assume success in most situations.
294 * @param pIf The interface instance.
295 * @param pSession The current session.
296 */
297DECLINLINE(void) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
298{
299 int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
300 AssertRC(rc);
301}
302
303
304/**
305 * RTHandleCreateEx callback that retains an object in the
306 * handle table before returning it.
307 *
308 * (Avoids racing the freeing of the handle.)
309 *
310 * @returns VBox status code.
311 * @param hHandleTable The handle table (ignored).
312 * @param pvObj The object (INTNETIF).
313 * @param pvCtx The context (SUPDRVSESSION).
314 * @param pvUser The user context (ignored).
315 */
316static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
317{
318 NOREF(pvUser);
319 NOREF(hHandleTable);
320 PINTNETIF pIf = (PINTNETIF)pvObj;
321 if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
322 return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
323 return VINF_SUCCESS;
324}
325
326
327/**
328 * Gets the address size of a network layer type.
329 *
330 * @returns size in bytes.
331 * @param enmType The type.
332 */
333DECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
334{
335 switch (enmType)
336 {
337 case kIntNetAddrType_IPv4: return 4;
338 case kIntNetAddrType_IPv6: return 16;
339 case kIntNetAddrType_IPX: return 4 + 6;
340 default: AssertFailedReturn(0);
341 }
342}
343
344
345/**
346 * Compares two address to see if they are equal, assuming naturally align structures.
347 *
348 * @returns true if equal, false if not.
349 * @param pAddr1 The first address.
350 * @param pAddr2 The second address.
351 * @param cbAddr The address size.
352 */
353DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
354{
355 switch (cbAddr)
356 {
357 case 4: /* IPv4 */
358 return pAddr1->au32[0] == pAddr2->au32[0];
359 case 16: /* IPv6 */
360 return pAddr1->au64[0] == pAddr2->au64[0]
361 && pAddr1->au64[1] == pAddr2->au64[1];
362 case 10: /* IPX */
363 return pAddr1->au64[0] == pAddr2->au64[0]
364 && pAddr1->au16[4] == pAddr2->au16[4];
365 default:
366 AssertFailedReturn(false);
367 }
368}
369
370
371/**
372 * Worker for intnetR0IfAddrCacheLookup that performs the lookup
373 * in the remaining cache entries after the caller has check the
374 * most likely ones.
375 *
376 * @returns -1 if not found, the index of the cache entry if found.
377 * @param pCache The cache.
378 * @param pAddr The address.
379 * @param cbAddr The address size (optimization).
380 */
381static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
382{
383 unsigned i = pCache->cEntries - 2;
384 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
385 while (i >= 1)
386 {
387 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
388 return i;
389 pbEntry -= pCache->cbEntry;
390 i--;
391 }
392
393 return -1;
394}
395
396/**
397 * Lookup an address in a cache without any expectations.
398 *
399 * @returns -1 if not found, the index of the cache entry if found.
400 * @param pCache The cache.
401 * @param pAddr The address.
402 * @param cbAddr The address size (optimization).
403 */
404DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
405{
406 Assert(pCache->cbAddress == cbAddr);
407
408 /*
409 * The optimized case is when there is one cache entry and
410 * it doesn't match.
411 */
412 unsigned i = pCache->cEntries;
413 if ( i > 0
414 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
415 return 0;
416 if (i <= 1)
417 return -1;
418
419 /*
420 * Check the last entry.
421 */
422 i--;
423 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
424 return i;
425 if (i <= 1)
426 return -1;
427
428 return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
429}
430
431
432/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
433DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
434{
435 /** @todo implement this. */
436 return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
437}
438
439
440/**
441 * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
442 * the lookup in the remaining cache entries after the caller
443 * has check the most likely ones.
444 *
445 * The routine is expecting not to find the address.
446 *
447 * @returns -1 if not found, the index of the cache entry if found.
448 * @param pCache The cache.
449 * @param pAddr The address.
450 * @param cbAddr The address size (optimization).
451 */
452static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
453{
454 /*
455 * Perform a full table lookup.
456 */
457 unsigned i = pCache->cEntries - 2;
458 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
459 while (i >= 1)
460 {
461 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
462 return i;
463 pbEntry -= pCache->cbEntry;
464 i--;
465 }
466
467 return -1;
468}
469
470
471/**
472 * Lookup an address in a cache expecting not to find it.
473 *
474 * @returns -1 if not found, the index of the cache entry if found.
475 * @param pCache The cache.
476 * @param pAddr The address.
477 * @param cbAddr The address size (optimization).
478 */
479DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
480{
481 Assert(pCache->cbAddress == cbAddr);
482
483 /*
484 * The optimized case is when there is one cache entry and
485 * it doesn't match.
486 */
487 unsigned i = pCache->cEntries;
488 if (RT_UNLIKELY( i > 0
489 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
490 return 0;
491 if (RT_LIKELY(i <= 1))
492 return -1;
493
494 /*
495 * Then check the last entry and return if there are just two cache entries.
496 */
497 i--;
498 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
499 return i;
500 if (i <= 1)
501 return -1;
502
503 return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
504}
505
506
507/**
508 * Deletes a specific cache entry.
509 *
510 * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
511 *
512 * @param pIf The interface (for logging).
513 * @param pCache The cache.
514 * @param iEntry The entry to delete.
515 */
516static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry)
517{
518 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 type=%d #%d %.*Rhxs\n", pIf->hIf,
519 (int)(intptr_t)(pCache - &pIf->aAddrCache[0]), iEntry, pCache->cbAddress, pCache->pbEntries + iEntry * pCache->cbEntry));
520 Assert(iEntry < pCache->cEntries);
521 pCache->cEntries--;
522 if (iEntry < pCache->cEntries)
523 memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
524 pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
525 (pCache->cEntries - iEntry) * pCache->cbEntry);
526}
527
528
529/**
530 * Deletes an address from the cache, assuming it isn't actually in the cache.
531 *
532 * @param pIf The interface (for logging).
533 * @param pCache The cache.
534 * @param pAddr The address.
535 * @param cbAddr The address size (optimization).
536 */
537DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
538{
539 int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
540 if (RT_UNLIKELY(i >= 0))
541 intnetR0IfAddrCacheDeleteIt(pIf, pCache, i);
542}
543
544
545/**
546 * Deletes the address from all the interface caches.
547 *
548 * This is used to remove stale entries that has been reassigned to
549 * other machines on the network.
550 *
551 * @param pNetwork The network.
552 * @param pAddr The address.
553 * @param enmType The address type.
554 * @param cbAddr The address size (optimization).
555 */
556DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr,
557 INTNETADDRTYPE const enmType, uint8_t const cbAddr)
558{
559 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
560 {
561 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
562 if (RT_UNLIKELY(i >= 0))
563 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i);
564 }
565}
566
567
568/**
569 * Deletes the address from all the interface caches except the specified one.
570 *
571 * This is used to remove stale entries that has been reassigned to
572 * other machines on the network.
573 *
574 * @param pNetwork The network.
575 * @param pAddr The address.
576 * @param enmType The address type.
577 * @param cbAddr The address size (optimization).
578 */
579DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
580 INTNETADDRTYPE const enmType, uint8_t const cbAddr)
581{
582 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
583 if (pIf != pIfSender)
584 {
585 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
586 if (RT_UNLIKELY(i >= 0))
587 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i);
588 }
589}
590
591
592/**
593 * Adds an address to the cache, the caller is responsible for making sure it'
594 * s not already in the cache.
595 *
596 * @param pIf The interface (for logging).
597 * @param pCache The address cache.
598 * @param pAddr The address.
599 */
600static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr)
601{
602 if (!pCache->cEntriesAlloc)
603 {
604 /* Allocate the first array */
605 pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cbEntry * 16);
606 if (!pCache->pbEntries)
607 return;
608 }
609 else
610 {
611 bool fReplace = true;
612 if (pCache->cEntriesAlloc < 64)
613 {
614 uint8_t cEntriesAlloc = pCache->cEntriesAlloc + 16;
615 void *pvNew = RTMemRealloc(pCache->pbEntries, pCache->cbEntry * cEntriesAlloc);
616 if (pvNew)
617 {
618 pCache->pbEntries = (uint8_t *)pvNew;
619 pCache->cEntriesAlloc = cEntriesAlloc;
620 fReplace = false;
621 }
622 }
623 if (fReplace)
624 {
625 /* simple FIFO, might consider usage/ageing here? */
626 Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
627 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
628 memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
629 pCache->cEntries--;
630 }
631 }
632
633 /*
634 * Add the new entry to the end of the array.
635 */
636 uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
637 memcpy(pbEntry, pAddr, pCache->cbAddress);
638 memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
639 Log(("intnetR0IfAddrCacheAddIt: type=%d added #%d %.*Rhxs\n",
640 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cEntries, pCache->cbAddress, pAddr));
641 pCache->cEntries++;
642 Assert(pCache->cEntries <= pCache->cEntriesAlloc);
643}
644
645
646/**
647 * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
648 *
649 * @param pIf The interface (for logging).
650 * @param pCache The address cache.
651 * @param pAddr The address.
652 * @param cbAddr The size of the address (optimization).
653 */
654static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
655{
656 /*
657 * Check all but the first and last entries, the caller
658 * has already checked those.
659 */
660 unsigned cLeft = pCache->cEntries;
661 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
662 while (--cLeft > 1)
663 {
664 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
665 {
666 /** @todo usage/ageing? */
667 return;
668 }
669 pbEntry += pCache->cbEntry;
670 cLeft--;
671 }
672
673 /*
674 * Not found, add it.
675 */
676 intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr);
677}
678
679
680/**
681 * Adds an address to the cache if it's not already there.
682 *
683 * @param pIf The interface (for logging).
684 * @param pCache The address cache.
685 * @param pAddr The address.
686 * @param cbAddr The size of the address (optimization).
687 */
688DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
689{
690 Assert(pCache->cbAddress == cbAddr);
691
692 /*
693 * The optimized case is when the address the first cache entry.
694 */
695 unsigned i = pCache->cEntries;
696 if (RT_LIKELY( i > 0
697 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
698 {
699 /** @todo usage/ageing? */
700 return;
701 }
702 if (i <= 1)
703 return;
704
705 /*
706 * And the case where it's the last entry.
707 */
708 i--;
709 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
710 {
711 /** @todo usage/ageing? */
712 return;
713 }
714 if (i <= 1)
715 return;
716
717 intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr);
718}
719
720
721/**
722 * Deals with an IPv4 packet.
723 *
724 * This will fish out the source IP address and add it to the cache.
725 * Then it will look for DHCPRELEASE requests (?) and anything else
726 * that we migh find useful later.
727 *
728 * @param pIf The interface that's sending the frame.
729 * @param pHdr Pointer to the IPv4 header in the frame.
730 * @param cbPacket The size of the packet, or more correctly the
731 * size of the frame without the ethernet header.
732 */
733static void intnetR0IfSniffIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pHdr, uint32_t cbPacket)
734{
735 /*
736 * Check the header size.
737 */
738 if (cbPacket < RT_UOFFSETOF(RTNETIPV4, ip_options)) /** @todo check minimum size requirements here. */
739 return;
740 uint32_t cbHdr = (uint32_t)pHdr->ip_hl * 4;
741 if ( cbHdr < RT_UOFFSETOF(RTNETIPV4, ip_options)
742 || cbPacket < cbHdr)
743 return;
744
745 /*
746 * Ignore 255.255.255.255 (broadcast), 0.0.0.0 (null) and already cached addresses.
747 */
748 RTNETADDRU Addr;
749 Addr.IPv4 = pHdr->ip_src;
750 if (Addr.au32[0] == UINT32_C(0xffffffff))
751 return;
752
753 int i = -1;
754 if ( Addr.au32[0] == 0
755 || (i = intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(pHdr->ip_src))) >= 0)
756 {
757#if 0 /** @todo quick DHCP check? */
758 if (pHdr->ip_p != RTNETIPV4_PROT_UDP)
759 return;
760#endif
761 return;
762 }
763
764 /*
765 * Check the header checksum.
766 */
767 /** @todo IP checksumming */
768
769 /*
770 * Add the source address.
771 */
772 if (i < 0)
773 intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr);
774
775 /*
776 * Check for DHCP (properly this time).
777 */
778 /** @todo DHCPRELEASE request? */
779}
780
781
782
783/**
784 * Sniff up source addresses from an ARP request or reply.
785 *
786 * @param pIf The interface that's sending the frame.
787 * @param pHdr The ARP header.
788 * @param cbPacket The size of the packet (migth be larger than the ARP
789 * request 'cause of min ethernet frame size).
790 */
791static void intnetR0IfSniffArpSourceAddr(PINTNETIF pIf, PCRTNETARPHDR pHdr, uint32_t cbPacket)
792{
793 /*
794 * Ignore malformed packets and packets which doesn't interrest us.
795 */
796 if (RT_UNLIKELY( cbPacket <= sizeof(*pHdr)
797 || pHdr->ar_hlen != sizeof(PDMMAC)
798 || pHdr->ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
799 || ( pHdr->ar_oper != RT_H2BE_U16(RTNET_ARPOP_REQUEST)
800 && pHdr->ar_oper != RT_H2BE_U16(RTNET_ARPOP_REPLY)
801 && pHdr->ar_oper != RT_H2BE_U16(RTNET_ARPOP_REVREQUEST)
802 && pHdr->ar_oper != RT_H2BE_U16(RTNET_ARPOP_REVREPLY)
803 /** @todo Read up on inverse ARP. */
804 )
805 )
806 )
807 return;
808
809 /*
810 * Deal with the protocols.
811 */
812 RTNETADDRU Addr;
813 if (pHdr->ar_ptype == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
814 {
815 /*
816 * IPv4.
817 */
818 PCRTNETARPIPV4 pReq = (PCRTNETARPIPV4)pHdr;
819 if (RT_UNLIKELY( pHdr->ar_plen == sizeof(pReq->ar_spa)
820 || cbPacket < sizeof(*pReq)
821 || pReq->ar_spa.u == 0 /** @todo add an inline function for checking that an IP address is worth caching. */
822 || pReq->ar_spa.u == UINT32_C(0xffffffff)))
823 return;
824
825 if (RTNET_ARPOP_IS_REPLY(RT_BE2H_U16(pHdr->ar_oper)))
826 {
827 Addr.IPv4 = pReq->ar_tpa;
828 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(pReq->ar_tpa));
829 }
830 Addr.IPv4 = pReq->ar_spa;
831 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(pReq->ar_spa));
832 }
833 else if (pHdr->ar_ptype == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
834 {
835 /*
836 * IPv6.
837 */
838 PCINTNETARPIPV6 pReq = (PCINTNETARPIPV6)pHdr;
839 if (RT_UNLIKELY( pHdr->ar_plen == sizeof(pReq->ar_spa)
840 || cbPacket < sizeof(*pReq)
841 /** @todo IPv6 || pReq->ar_spa.au32[0] == 0 or something
842 || pReq->ar_spa.au32[0] == UINT32_C(0xffffffff)*/))
843 return;
844
845 if (RTNET_ARPOP_IS_REPLY(RT_BE2H_U16(pHdr->ar_oper)))
846 {
847 Addr.IPv6 = pReq->ar_tpa;
848 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv6], &Addr, sizeof(pReq->ar_tpa));
849 }
850 Addr.IPv6 = pReq->ar_spa;
851 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv6], &Addr, sizeof(pReq->ar_spa));
852 }
853 else
854 Log(("intnetR0IfSniffArpSourceAddr: unknown ar_ptype=%#x\n", RT_BE2H_U16(pHdr->ar_ptype)));
855}
856
857
858
859/**
860 * Checks packets send by a normal interface for new network
861 * layer addresses.
862 *
863 * @param pIf The interface that's sending the frame.
864 * @param pbFrame The frame.
865 * @param cbFrame The size of the frame.
866 */
867static void intnetR0IfSniffSourceAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame)
868{
869 /*
870 * Fish out the ethertype and look for stuff we can handle.
871 */
872 if (cbFrame <= sizeof(RTNETETHERHDR))
873 return;
874 cbFrame -= sizeof(RTNETETHERHDR);
875
876 uint16_t EtherType = ((PCRTNETETHERHDR)pbFrame)->EtherType;
877 if (EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV4))
878 intnetR0IfSniffIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
879#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
880 else if (EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_IPV6))
881 intnetR0IfSniffIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
882#endif
883#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
884 else if ( EtherType == RT_H2BE_U16(0x8037) //??
885 || EtherType == RT_H2BE_U16(0x8137) //??
886 || EtherType == RT_H2BE_U16(0x8138) //??
887 )
888 intnetR0IfSniffIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
889#endif
890 else if (EtherType == RT_H2BE_U16(RTNET_ETHERTYPE_ARP))
891 intnetR0IfSniffArpSourceAddr(pIf, (PCRTNETARPHDR)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
892}
893
894
895/**
896 * Initializes a scatter / gather buffer from a simple linear buffer.
897 *
898 * @returns Pointer to the start of the frame.
899 * @param pSG Pointer to the scatter / gather structure.
900 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
901 * @param pvFrame Pointer to the frame
902 * @param cbFrame The size of the frame.
903 */
904DECLINLINE(void) intnetR0SgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
905{
906 pSG->pvOwnerData = NULL;
907 pSG->pvUserData = NULL;
908 pSG->pvUserData2 = NULL;
909 pSG->cbTotal = cbFrame;
910 pSG->cUsers = 1;
911 pSG->fFlags = INTNETSG_FLAGS_TEMP;
912 pSG->cSegsAlloc = 1;
913 pSG->cSegsUsed = 1;
914 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
915 pSG->aSegs[0].pv = pvFrame;
916 pSG->aSegs[0].cb = cbFrame;
917}
918
919
920/**
921 * Initializes a scatter / gather buffer from a internal networking packet.
922 *
923 * @returns Pointer to the start of the frame.
924 * @param pSG Pointer to the scatter / gather structure.
925 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
926 * @param pHdr Pointer to the packet header.
927 * @param pBuf The buffer the header is within. Only used in strict builds.
928 */
929DECLINLINE(void) intnetR0SgInitFromPkt(PINTNETSG pSG, PCINTNETHDR pPktHdr, PCINTNETBUF pBuf)
930{
931 pSG->cSegsUsed = 1;
932 pSG->cbTotal = pSG->aSegs[0].cb = pPktHdr->cbFrame;
933 pSG->aSegs[0].pv = INTNETHdrGetFramePtr(pPktHdr, pBuf);
934 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
935}
936
937
938#ifdef IN_INTNET_TESTCASE
939/**
940 * Reads the next frame in the buffer.
941 * The caller is responsible for ensuring that there is a valid frame in the buffer.
942 *
943 * @returns Size of the frame in bytes.
944 * @param pBuf The buffer.
945 * @param pRingBuff The ring buffer to read from.
946 * @param pvFrame Where to put the frame. The caller is responsible for
947 * ensuring that there is sufficient space for the frame.
948 */
949static unsigned intnetR0RingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
950{
951 Assert(pRingBuf->offRead < pBuf->cbBuf);
952 Assert(pRingBuf->offRead >= pRingBuf->offStart);
953 Assert(pRingBuf->offRead < pRingBuf->offEnd);
954 uint32_t offRead = pRingBuf->offRead;
955 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offRead);
956 const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
957 unsigned cb = pHdr->cbFrame;
958 memcpy(pvFrame, pvFrameIn, cb);
959
960 /* skip the frame */
961 offRead += pHdr->offFrame + cb;
962 offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
963 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
964 if (offRead >= pRingBuf->offEnd)
965 offRead = pRingBuf->offStart;
966 ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
967 return cb;
968}
969#endif /* IN_INTNET_TESTCASE */
970
971
972/**
973 * Reads an entire SG into a fittingly size buffer.
974 *
975 * @param pSG The SG list to read.
976 * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
977 */
978DECLINLINE(void) intnetR0SgRead(PCINTNETSG pSG, void *pvBuf)
979{
980 if (pSG->cSegsUsed == 1)
981 {
982 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
983 memcpy(pvBuf, pSG->aSegs[0].pv, pSG->cbTotal);
984 }
985 else
986 {
987 uint8_t *pbDst = (uint8_t *)pvBuf;
988 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
989 for (unsigned iSeg = 0; iSeg < cSegs; iSeg++)
990 {
991 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
992 Assert(cbSeg <= pSG->cbTotal && (uintptr_t)(pbDst - (uint8_t *)pvBuf) + cbSeg <= pSG->cbTotal);
993 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
994 pbDst += cbSeg;
995 }
996 }
997}
998
999
1000/**
1001 * Writes a frame packet to the buffer.
1002 *
1003 * @returns VBox status code.
1004 * @param pBuf The buffer.
1005 * @param pRingBuf The ring buffer to read from.
1006 * @param pSG The gatter list.
1007 */
1008static int intnetR0RingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, PCINTNETSG pSG)
1009{
1010 /*
1011 * Validate input.
1012 */
1013 AssertPtr(pBuf);
1014 AssertPtr(pRingBuf);
1015 AssertPtr(pSG);
1016 Assert(pSG->cbTotal >= sizeof(PDMMAC) * 2);
1017 uint32_t offWrite = pRingBuf->offWrite;
1018 Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
1019 uint32_t offRead = pRingBuf->offRead;
1020 Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
1021
1022 const uint32_t cb = RT_ALIGN_32(pSG->cbTotal, sizeof(INTNETHDR));
1023 if (offRead <= offWrite)
1024 {
1025 /*
1026 * Try fit it all before the end of the buffer.
1027 */
1028 if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
1029 {
1030 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1031 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1032 pHdr->cbFrame = pSG->cbTotal;
1033 pHdr->offFrame = sizeof(INTNETHDR);
1034
1035 intnetR0SgRead(pSG, pHdr + 1);
1036
1037 offWrite += cb + sizeof(INTNETHDR);
1038 Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
1039 if (offWrite >= pRingBuf->offEnd)
1040 offWrite = pRingBuf->offStart;
1041 Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
1042 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1043 return VINF_SUCCESS;
1044 }
1045
1046 /*
1047 * Try fit the frame at the start of the buffer.
1048 * (The header fits before the end of the buffer because of alignment.)
1049 */
1050 AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
1051 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
1052 {
1053 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1054 void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
1055 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1056 pHdr->cbFrame = pSG->cbTotal;
1057 pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
1058
1059 intnetR0SgRead(pSG, pvFrameOut);
1060
1061 offWrite = pRingBuf->offStart + cb;
1062 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1063 Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
1064 return VINF_SUCCESS;
1065 }
1066 }
1067 /*
1068 * The reader is ahead of the writer, try fit it into that space.
1069 */
1070 else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
1071 {
1072 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1073 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1074 pHdr->cbFrame = pSG->cbTotal;
1075 pHdr->offFrame = sizeof(INTNETHDR);
1076
1077 intnetR0SgRead(pSG, pHdr + 1);
1078
1079 offWrite += cb + sizeof(INTNETHDR);
1080 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1081 Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
1082 return VINF_SUCCESS;
1083 }
1084
1085 /* (it didn't fit) */
1086 /** @todo stats */
1087 return VERR_BUFFER_OVERFLOW;
1088}
1089
1090
1091/**
1092 * Sends a frame to a specific interface.
1093 *
1094 * @param pIf The interface.
1095 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1096 * @param pSG The gather buffer which data is being sent to the interface.
1097 */
1098static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG)
1099{
1100// LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
1101 int rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG);
1102 if (RT_SUCCESS(rc))
1103 {
1104 pIf->cYields = 0;
1105 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1106 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1107 RTSemEventSignal(pIf->Event);
1108 return;
1109 }
1110
1111 Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
1112
1113#if 0 /* This is bad stuff now as we're blocking while locking down the network.
1114 we really shouldn't delay the network traffic on the host just because
1115 some bugger isn't responding. Will have to deal with this in a different
1116 manner if required. */
1117 /*
1118 * Retry a few times, yielding the CPU in between.
1119 * But don't let a unresponsive VM harm performance, so give up after a couple of tries.
1120 */
1121 if ( pIf->fActive
1122 && pIf->cYields < 100)
1123 {
1124 unsigned cYields = 10;
1125#else
1126 /*
1127 * Scheduling hack, for unicore machines primarily.
1128 */
1129 if ( pIf->fActive
1130 && pIf->cYields < 4 /* just twice */
1131 && pIfSender /* but not if it's from the trunk */)
1132 {
1133 unsigned cYields = 2;
1134#endif
1135 while (--cYields > 0)
1136 {
1137 RTSemEventSignal(pIf->Event);
1138 RTThreadYield();
1139 rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG);
1140 if (RT_SUCCESS(rc))
1141 {
1142 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
1143 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1144 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1145 RTSemEventSignal(pIf->Event);
1146 return;
1147 }
1148 pIf->cYields++;
1149 }
1150 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
1151 }
1152
1153 /* ok, the frame is lost. */
1154 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
1155 RTSemEventSignal(pIf->Event);
1156}
1157
1158
1159/**
1160 * Sends a frame down the trunk.
1161 *
1162 * The caller must own the network mutex, might be abandond temporarily.
1163 * The fTrunkLock parameter indicates whether the trunk lock is held.
1164 *
1165 * @param pThis The trunk.
1166 * @param pNetwork The network the frame is being sent to.
1167 * @param fDst The destination flags.
1168 * @param pSG Pointer to the gather list.
1169 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1170 */
1171static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, uint32_t fDst, PINTNETSG pSG, bool fTrunkLocked)
1172{
1173 /*
1174 * Quick sanity check.
1175 */
1176 AssertPtr(pThis);
1177 AssertPtr(pNetwork);
1178 AssertPtr(pSG);
1179 Assert(fDst);
1180 AssertReturnVoid(pThis->pIfPort);
1181
1182 /*
1183 * Temporarily leave the network lock while transmitting the frame.
1184 *
1185 * Note that we're relying on the out-bound lock to serialize threads down
1186 * in INTNETR0IfSend. It's theoretically possible for there to be race now
1187 * because I didn't implement async SG handling yet. Which is why we currently
1188 * require the trunk to be locked, well, one of the reasons.
1189 *
1190 * Another reason is that the intnetR0NetworkSendUnicast code may have to
1191 * call into the trunk interface component to do package switching.
1192 */
1193 AssertReturnVoid(fTrunkLocked); /* to be removed. */
1194
1195 int rc;
1196 if ( fTrunkLocked
1197 || intnetR0TrunkIfRetain(pThis))
1198 {
1199 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1200 AssertRC(rc);
1201 if (RT_SUCCESS(rc))
1202 {
1203 if ( fTrunkLocked
1204 || intnetR0TrunkIfOutLock(pThis))
1205 {
1206 rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
1207
1208 if (!fTrunkLocked)
1209 intnetR0TrunkIfOutUnlock(pThis);
1210 }
1211 else
1212 {
1213 AssertFailed();
1214 rc = VERR_SEM_DESTROYED;
1215 }
1216
1217 int rc2 = RTSemFastMutexRequest(pNetwork->FastMutex);
1218 AssertRC(rc2);
1219 }
1220
1221 if (!fTrunkLocked)
1222 intnetR0TrunkIfRelease(pThis);
1223 }
1224 else
1225 {
1226 AssertFailed();
1227 rc = VERR_SEM_DESTROYED;
1228 }
1229
1230 /** @todo failure statistics? */
1231 Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst));
1232}
1233
1234
1235/**
1236 * Sends a broadcast frame.
1237 *
1238 * The caller must own the network mutex, might be abandond temporarily.
1239 * When pIfSender is not NULL, the caller must also own the trunk semaphore.
1240 *
1241 * @returns true if it's addressed to someone on the network, otherwise false.
1242 * @param pNetwork The network the frame is being sent to.
1243 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1244 * @param fSrc The source flags. This 0 if it's not from the trunk.
1245 * @param pSG Pointer to the gather list.
1246 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1247 */
1248static bool intnetR0NetworkSendBroadcast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PINTNETSG pSG, bool fTrunkLocked)
1249{
1250 /*
1251 * This is a broadcast or multicast address. For the present we treat those
1252 * two as the same - investigating multicast is left for later.
1253 *
1254 * Write the packet to all the interfaces and signal them.
1255 */
1256 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1257 if (pIf != pIfSender)
1258 intnetR0IfSend(pIf, pIfSender, pSG);
1259
1260 /*
1261 * Unless the trunk is the origin, broadcast it to both the wire
1262 * and the host as well.
1263 */
1264 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
1265 if ( pIfSender
1266 && pTrunkIf)
1267 intnetR0TrunkIfSend(pTrunkIf, pNetwork, INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked);
1268 return false; /* broadcast frames are never dropped */
1269}
1270
1271
1272/**
1273 * Sends a multicast frame.
1274 *
1275 * The caller must own the network mutex, might be abandond temporarily.
1276 *
1277 * @returns true if it's addressed to someone on the network, otherwise false.
1278 * @param pNetwork The network the frame is being sent to.
1279 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1280 * @param fSrc The source flags. This 0 if it's not from the trunk.
1281 * @param pSG Pointer to the gather list.
1282 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1283 * @param pEthHdr Pointer to the ethernet header.
1284 */
1285static bool intnetR0NetworkSendMulticast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
1286{
1287 /** @todo implement multicast */
1288 return intnetR0NetworkSendBroadcast(pNetwork, pIfSender, pSG, fTrunkLocked);
1289}
1290
1291
1292/**
1293 * Sends a unicast frame.
1294 *
1295 * The caller must own the network mutex, might be abandond temporarily.
1296 *
1297 * @returns true if it's addressed to someone on the network, otherwise false.
1298 * @param pNetwork The network the frame is being sent to.
1299 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1300 * @param fSrc The source flags. This 0 if it's not from the trunk.
1301 * @param pSG Pointer to the gather list.
1302 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1303 * @param pEthHdr Pointer to the ethernet header.
1304 */
1305static bool intnetR0NetworkSendUnicast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
1306{
1307 /*
1308 * Only send to the interfaces with matching a MAC address.
1309 */
1310 bool fExactIntNetRecipient = false;
1311 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1312 {
1313 bool fIt = false;
1314 if ( ( !pIf->fMacSet
1315 || (fIt = !memcmp(&pIf->Mac, &pEthHdr->DstMac, sizeof(pIf->Mac))) )
1316 || ( pIf->fPromiscuous
1317 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))
1318 && pIf != pIfSender /* promiscuous mode: omit the sender */))
1319 {
1320 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1321 fExactIntNetRecipient |= fIt;
1322 intnetR0IfSend(pIf, pIfSender, pSG);
1323 }
1324 }
1325
1326 /*
1327 * Send it to the trunk?
1328 * If we didn't find the recipient on the internal network the
1329 * frame will hit the wire.
1330 */
1331 uint32_t fDst = 0;
1332 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
1333 if ( pIfSender
1334 && pTrunkIf
1335 && pTrunkIf->pIfPort)
1336 {
1337 /* promiscuous checks first as they are cheaper than pfnIsHostMac. */
1338 if ( pTrunkIf->fPromiscuousWire
1339 && !(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)) )
1340 fDst |= INTNETTRUNKDIR_WIRE;
1341 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))
1342 || pTrunkIf->pIfPort->pfnIsPromiscuous(pTrunkIf->pIfPort) )
1343 fDst |= INTNETTRUNKDIR_HOST;
1344
1345 if ( fDst != (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
1346 && !fExactIntNetRecipient /* if you have duplicate mac addresses, you're screwed. */ )
1347 {
1348 if (pTrunkIf->pIfPort->pfnIsHostMac(pTrunkIf->pIfPort, &pEthHdr->DstMac))
1349 fDst |= INTNETTRUNKDIR_HOST;
1350 else
1351 fDst |= INTNETTRUNKDIR_WIRE;
1352 }
1353
1354 if (fDst)
1355 intnetR0TrunkIfSend(pTrunkIf, pNetwork, fDst, pSG, fTrunkLocked);
1356 }
1357
1358 /* log it */
1359 if ( !fExactIntNetRecipient
1360 && !fDst
1361 && ( (pEthHdr->DstMac.au8[0] == 0x08 && pEthHdr->DstMac.au8[1] == 0x00 && pEthHdr->DstMac.au8[2] == 0x27)
1362 || (pEthHdr->SrcMac.au8[0] == 0x08 && pEthHdr->SrcMac.au8[1] == 0x00 && pEthHdr->SrcMac.au8[2] == 0x27)))
1363 Log2(("Dst=%.6Rhxs ??\n", &pEthHdr->DstMac));
1364
1365 return fExactIntNetRecipient;
1366}
1367
1368
1369/**
1370 * Sends a frame.
1371 *
1372 * This function will distribute the frame to the interfaces it is addressed to.
1373 * It will also update the MAC address of the sender.
1374 *
1375 * The caller must own the network mutex.
1376 *
1377 * @returns true if it's addressed to someone on the network, otherwise false.
1378 * @param pNetwork The network the frame is being sent to.
1379 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1380 * @param fSrc The source flags. This 0 if it's not from the trunk.
1381 * @param pSG Pointer to the gather list.
1382 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1383 */
1384static bool intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked)
1385{
1386 bool fRc = false;
1387
1388 /*
1389 * Assert reality.
1390 */
1391 AssertPtr(pNetwork);
1392 AssertPtrNull(pIfSender);
1393 Assert(pIfSender ? fSrc == 0 : fSrc != 0);
1394 Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
1395 AssertPtr(pSG);
1396 Assert(pSG->cSegsUsed >= 1);
1397 Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
1398 if (pSG->cbTotal < sizeof(RTNETETHERHDR))
1399 return fRc;
1400
1401 /*
1402 * Send statistics.
1403 */
1404 if (pIfSender)
1405 {
1406 STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
1407 STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, pSG->cbTotal);
1408 }
1409
1410 /*
1411 * Get the ethernet header (might theoretically involve multiple segments).
1412 */
1413 RTNETETHERHDR EthHdr;
1414 if (RT_LIKELY(pSG->aSegs[0].cb >= sizeof(EthHdr)))
1415 EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
1416 else
1417 {
1418 uint8_t *pbDst = (uint8_t *)&EthHdr;
1419 size_t cbLeft = sizeof(EthHdr);
1420 for (unsigned iSeg = 0; cbLeft && iSeg < pSG->cSegsUsed; iSeg++)
1421 {
1422 size_t cb = RT_MIN(cbLeft, pSG->aSegs[iSeg].cb);
1423 memcpy(pbDst, pSG->aSegs[iSeg].pv, cb);
1424 pbDst += cb;
1425 cbLeft -= cb;
1426 }
1427 AssertReturn(!cbLeft, false);
1428 }
1429 if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
1430 || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
1431 || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
1432 || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
1433 || EthHdr.DstMac.au8[0] == 0xff
1434 || EthHdr.SrcMac.au8[0] == 0xff)
1435 Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
1436 &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
1437
1438 /*
1439 * Inspect the header updating the mac address of the sender in the process.
1440 */
1441 if ( pIfSender
1442 && memcmp(&EthHdr.SrcMac, &pIfSender->Mac, sizeof(pIfSender->Mac)))
1443 {
1444 /** @todo stats */
1445 Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &EthHdr.SrcMac));
1446 pIfSender->Mac = EthHdr.SrcMac;
1447 pIfSender->fMacSet = true;
1448 }
1449
1450 /*
1451 * Distribute the frame.
1452 */
1453 if (RT_UNLIKELY(EthHdr.DstMac.au8[0] & 1)) /* multicast address */
1454 fRc = intnetR0NetworkSendMulticast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
1455 else if ( EthHdr.DstMac.au16[0] == 0xffff /* broadcast address. */
1456 && EthHdr.DstMac.au16[1] == 0xffff
1457 && EthHdr.DstMac.au16[2] == 0xffff)
1458 fRc = intnetR0NetworkSendBroadcast(pNetwork, pIfSender, pSG, fTrunkLocked);
1459 else
1460 fRc = intnetR0NetworkSendUnicast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
1461 return fRc;
1462}
1463
1464
1465/**
1466 * Sends one or more frames.
1467 *
1468 * The function will first the frame which is passed as the optional
1469 * arguments pvFrame and cbFrame. These are optional since it also
1470 * possible to chain together one or more frames in the send buffer
1471 * which the function will process after considering it's arguments.
1472 *
1473 * @returns VBox status code.
1474 * @param pIntNet The instance data.
1475 * @param hIf The interface handle.
1476 * @param pSession The caller's session.
1477 * @param pvFrame Pointer to the frame. Optional, please don't use.
1478 * @param cbFrame Size of the frame. Optional, please don't use.
1479 */
1480INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, const void *pvFrame, unsigned cbFrame)
1481{
1482 Log5(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
1483
1484 /*
1485 * Validate input and translate the handle.
1486 */
1487 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
1488 if (pvFrame && cbFrame)
1489 {
1490 AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
1491 AssertPtrReturn(pvFrame, VERR_INVALID_PARAMETER);
1492 AssertPtrReturn((uint8_t *)pvFrame + cbFrame - 1, VERR_INVALID_PARAMETER);
1493
1494 /* This is the better place to crash, probe the buffer. */
1495 ASMProbeReadBuffer(pvFrame, cbFrame);
1496 }
1497 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1498 if (!pIf)
1499 return VERR_INVALID_HANDLE;
1500
1501 /*
1502 * Lock the network. If there is a trunk retain it and grab its
1503 * out-bound lock (this requires leaving the network lock first).
1504 * Grabbing the out-bound lock here simplifies things quite a bit
1505 * later on, so while this is excessive and a bit expensive it's
1506 * not worth caring about right now.
1507 */
1508 PINTNETNETWORK pNetwork = pIf->pNetwork;
1509 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
1510 if (RT_FAILURE(rc))
1511 {
1512 intnetR0IfRelease(pIf, pSession);
1513 return rc;
1514 }
1515 PINTNETTRUNKIF pTrunkIf = intnetR0TrunkIfRetain(pNetwork->pTrunkIF);
1516 if (pTrunkIf)
1517 {
1518 RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
1519
1520 if (!intnetR0TrunkIfOutLock(pTrunkIf))
1521 {
1522 intnetR0TrunkIfRelease(pTrunkIf);
1523 intnetR0IfRelease(pIf, pSession);
1524 return VERR_SEM_DESTROYED;
1525 }
1526
1527 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
1528 if (RT_FAILURE(rc))
1529 {
1530 intnetR0TrunkIfOutUnlock(pTrunkIf);
1531 intnetR0TrunkIfRelease(pTrunkIf);
1532 intnetR0IfRelease(pIf, pSession);
1533 return rc;
1534 }
1535 }
1536
1537 INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
1538 * with buffer sharing for some OS or service. Darwin copies everything so
1539 * I won't bother allocating and managing SGs rigth now. Sorry. */
1540
1541 /*
1542 * Process the argument.
1543 */
1544 if (pvFrame && cbFrame)
1545 {
1546 intnetR0SgInitTemp(&Sg, (void *)pvFrame, cbFrame);
1547 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
1548 }
1549
1550 /*
1551 * Process the send buffer.
1552 */
1553 while (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
1554 {
1555 /* Send the frame if the type is sane. */
1556 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
1557 if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
1558 {
1559 void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
1560 if (pvCurFrame)
1561 {
1562 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1563 intnetR0IfSniffSourceAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame);
1564 intnetR0SgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
1565 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
1566 }
1567 }
1568 /* else: ignore the frame */
1569
1570 /* Skip to the next frame. */
1571 INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
1572 }
1573
1574 /*
1575 * Release the semaphore(s) and release references.
1576 */
1577 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1578 if (pTrunkIf)
1579 {
1580 intnetR0TrunkIfOutUnlock(pTrunkIf);
1581 intnetR0TrunkIfRelease(pTrunkIf);
1582 }
1583
1584 intnetR0IfRelease(pIf, pSession);
1585 return rc;
1586}
1587
1588
1589/**
1590 * VMMR0 request wrapper for INTNETR0IfSend.
1591 *
1592 * @returns see INTNETR0IfSend.
1593 * @param pIntNet The internal networking instance.
1594 * @param pSession The caller's session.
1595 * @param pReq The request packet.
1596 */
1597INTNETR0DECL(int) INTNETR0IfSendReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
1598{
1599 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
1600 return VERR_INVALID_PARAMETER;
1601 return INTNETR0IfSend(pIntNet, pReq->hIf, pSession, NULL, 0);
1602}
1603
1604
1605/**
1606 * Maps the default buffer into ring 3.
1607 *
1608 * @returns VBox status code.
1609 * @param pIntNet The instance data.
1610 * @param hIf The interface handle.
1611 * @param pSession The caller's session.
1612 * @param ppRing3Buf Where to store the address of the ring-3 mapping.
1613 */
1614INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
1615{
1616 LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
1617
1618 /*
1619 * Validate input.
1620 */
1621 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
1622 AssertPtrReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
1623 *ppRing3Buf = 0;
1624 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1625 if (!pIf)
1626 return VERR_INVALID_HANDLE;
1627
1628 /*
1629 * ASSUMES that only the process that created an interface can use it.
1630 * ASSUMES that we created the ring-3 mapping when selecting or
1631 * allocating the buffer.
1632 */
1633 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
1634 if (RT_SUCCESS(rc))
1635 {
1636 *ppRing3Buf = pIf->pIntBufR3;
1637 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
1638 }
1639
1640 intnetR0IfRelease(pIf, pSession);
1641 LogFlow(("INTNETR0IfGetRing3Buffer: returns %Rrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
1642 return rc;
1643}
1644
1645
1646/**
1647 * VMMR0 request wrapper for INTNETR0IfGetRing3Buffer.
1648 *
1649 * @returns see INTNETR0IfGetRing3Buffer.
1650 * @param pIntNet The internal networking instance.
1651 * @param pSession The caller's session.
1652 * @param pReq The request packet.
1653 */
1654INTNETR0DECL(int) INTNETR0IfGetRing3BufferReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFGETRING3BUFFERREQ pReq)
1655{
1656 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
1657 return VERR_INVALID_PARAMETER;
1658 return INTNETR0IfGetRing3Buffer(pIntNet, pReq->hIf, pSession, &pReq->pRing3Buf);
1659}
1660
1661
1662/**
1663 * Gets the ring-0 address of the current buffer.
1664 *
1665 * @returns VBox status code.
1666 * @param pIntNet The instance data.
1667 * @param hIf The interface handle.
1668 * @param pSession The caller's session.
1669 * @param ppRing0Buf Where to store the address of the ring-3 mapping.
1670 */
1671INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF *ppRing0Buf)
1672{
1673 LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
1674
1675 /*
1676 * Validate input.
1677 */
1678 AssertPtrReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
1679 *ppRing0Buf = NULL;
1680 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
1681 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1682 if (!pIf)
1683 return VERR_INVALID_HANDLE;
1684
1685 /*
1686 * Grab the lock and get the data.
1687 * ASSUMES that the handle isn't closed while we're here.
1688 */
1689 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
1690 if (RT_SUCCESS(rc))
1691 {
1692 *ppRing0Buf = pIf->pIntBuf;
1693
1694 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
1695 }
1696 intnetR0IfRelease(pIf, pSession);
1697 LogFlow(("INTNETR0IfGetRing0Buffer: returns %Rrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
1698 return rc;
1699}
1700
1701
1702#if 0
1703/**
1704 * Gets the physical addresses of the default interface buffer.
1705 *
1706 * @returns VBox status code.
1707 * @param pIntNet The instance data.
1708 * @param hIF The interface handle.
1709 * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
1710 * @param cPages
1711 */
1712INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
1713{
1714 /*
1715 * Validate input.
1716 */
1717 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
1718 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
1719 AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
1720 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1721 if (!pIf)
1722 return VERR_INVALID_HANDLE;
1723
1724 /*
1725 * Grab the lock and get the data.
1726 * ASSUMES that the handle isn't closed while we're here.
1727 */
1728 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
1729 if (RT_SUCCESS(rc))
1730 {
1731 /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
1732 * is no need for any extra bookkeeping here.. */
1733
1734 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
1735 }
1736 intnetR0IfRelease(pIf, pSession);
1737 return VERR_NOT_IMPLEMENTED;
1738}
1739#endif
1740
1741
1742/**
1743 * Sets the promiscuous mode property of an interface.
1744 *
1745 * @returns VBox status code.
1746 * @param pIntNet The instance handle.
1747 * @param hIf The interface handle.
1748 * @param pSession The caller's session.
1749 * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
1750 */
1751INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
1752{
1753 LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
1754
1755 /*
1756 * Validate & translate input.
1757 */
1758 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
1759 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1760 if (!pIf)
1761 {
1762 Log(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
1763 return VERR_INVALID_HANDLE;
1764 }
1765
1766 /*
1767 * Grab the network semaphore and make the change.
1768 */
1769 int rc;
1770 PINTNETNETWORK pNetwork = pIf->pNetwork;
1771 if (pNetwork)
1772 {
1773 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
1774 if (RT_SUCCESS(rc))
1775 {
1776 if (pIf->fPromiscuous != fPromiscuous)
1777 {
1778 Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
1779 hIf, !fPromiscuous, !!fPromiscuous));
1780 ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
1781 }
1782
1783 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1784 }
1785 }
1786 else
1787 rc = VERR_WRONG_ORDER;
1788
1789 intnetR0IfRelease(pIf, pSession);
1790 return rc;
1791}
1792
1793
1794/**
1795 * VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode.
1796 *
1797 * @returns see INTNETR0IfSetPromiscuousMode.
1798 * @param pIntNet The internal networking instance.
1799 * @param pSession The caller's session.
1800 * @param pReq The request packet.
1801 */
1802INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
1803{
1804 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
1805 return VERR_INVALID_PARAMETER;
1806 return INTNETR0IfSetPromiscuousMode(pIntNet, pReq->hIf, pSession, pReq->fPromiscuous);
1807}
1808
1809
1810/**
1811 * Sets the MAC address of an interface.
1812 *
1813 * @returns VBox status code.
1814 * @param pIntNet The instance handle.
1815 * @param hIf The interface handle.
1816 * @param pSession The caller's session.
1817 * @param pMAC The new MAC address.
1818 */
1819INTNETR0DECL(int) INTNETR0IfSetMacAddress(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCPDMMAC pMac)
1820{
1821 LogFlow(("INTNETR0IfSetMacAddress: pIntNet=%p hIf=%RX32 pMac=%p:{%.6Rhxs}\n", pIntNet, hIf, pMac, pMac));
1822
1823 /*
1824 * Validate & translate input.
1825 */
1826 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
1827 AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
1828 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1829 if (!pIf)
1830 {
1831 Log(("INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
1832 return VERR_INVALID_HANDLE;
1833 }
1834
1835 /*
1836 * Grab the network semaphore and make the change.
1837 */
1838 int rc;
1839 PINTNETNETWORK pNetwork = pIf->pNetwork;
1840 if (pNetwork)
1841 {
1842 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
1843 if (RT_SUCCESS(rc))
1844 {
1845 if (memcmp(&pIf->Mac, pMac, sizeof(pIf->Mac)))
1846 {
1847 Log(("INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
1848 hIf, &pIf->Mac, pMac));
1849 pIf->Mac = *pMac;
1850 pIf->fMacSet = true;
1851 }
1852
1853 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1854 }
1855 }
1856 else
1857 rc = VERR_WRONG_ORDER;
1858
1859 intnetR0IfRelease(pIf, pSession);
1860 return rc;
1861}
1862
1863
1864/**
1865 * VMMR0 request wrapper for INTNETR0IfSetMacAddress.
1866 *
1867 * @returns see INTNETR0IfSetMacAddress.
1868 * @param pIntNet The internal networking instance.
1869 * @param pSession The caller's session.
1870 * @param pReq The request packet.
1871 */
1872INTNETR0DECL(int) INTNETR0IfSetMacAddressReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
1873{
1874 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
1875 return VERR_INVALID_PARAMETER;
1876 return INTNETR0IfSetMacAddress(pIntNet, pReq->hIf, pSession, &pReq->Mac);
1877}
1878
1879
1880/**
1881 * Worker for intnetR0IfSetActive.
1882 *
1883 * This function will update the active interface count on the network and
1884 * activate or deactivate the trunk connection if necessary. Note that in
1885 * order to do this it is necessary to abandond the network semaphore.
1886 *
1887 * @returns VBox status code.
1888 * @param pNetwork The network.
1889 * @param fIf The interface.
1890 * @param fActive What to do.
1891 */
1892static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
1893{
1894 /* quick santiy check */
1895 AssertPtr(pNetwork);
1896 AssertPtr(pIf);
1897
1898 /*
1899 * If we've got a trunk, lock it now in case we need to call out, and
1900 * then lock the network.
1901 */
1902 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
1903 if (pTrunkIf && !intnetR0TrunkIfOutLock(pTrunkIf))
1904 return VERR_SEM_DESTROYED;
1905
1906 int rc = RTSemFastMutexRequest(pNetwork->FastMutex); AssertRC(rc);
1907 if (RT_SUCCESS(rc))
1908 {
1909 bool fNetworkLocked = true;
1910
1911 /*
1912 * Make the change if necessary.
1913 */
1914 if (pIf->fActive != fActive)
1915 {
1916 pIf->fActive = fActive;
1917
1918 uint32_t const cActiveIFs = pNetwork->cActiveIFs;
1919 Assert((int32_t)cActiveIFs + (fActive ? 1 : -1) >= 0);
1920 pNetwork->cActiveIFs += fActive ? 1 : -1;
1921
1922 if ( pTrunkIf
1923 && ( !pNetwork->cActiveIFs
1924 || !cActiveIFs))
1925 {
1926 /*
1927 * We'll have to change the trunk status, so, leave
1928 * the network semaphore so we don't create any deadlocks.
1929 */
1930 int rc2 = RTSemFastMutexRelease(pNetwork->FastMutex); AssertRC(rc2);
1931 fNetworkLocked = false;
1932
1933 if (pTrunkIf->pIfPort)
1934 pTrunkIf->pIfPort->pfnSetActive(pTrunkIf->pIfPort, fActive);
1935 }
1936 }
1937
1938 if (fNetworkLocked)
1939 RTSemFastMutexRelease(pNetwork->FastMutex);
1940 }
1941 if (pTrunkIf)
1942 intnetR0TrunkIfOutUnlock(pTrunkIf);
1943 return rc;
1944}
1945
1946
1947/**
1948 * Activates or deactivates a interface.
1949 *
1950 * This is used to enable and disable the trunk connection on demans as well as
1951 * know when not to expect an interface to want to receive packets.
1952 *
1953 * @returns VBox status code.
1954 * @param pIf The interface.
1955 * @param fActive What to do.
1956 */
1957static int intnetR0IfSetActive(PINTNETIF pIf, bool fActive)
1958{
1959 /* quick sanity check */
1960 AssertPtrReturn(pIf, VERR_INVALID_POINTER);
1961
1962 /*
1963 * Hand it to the network since it might involve the trunk
1964 * and things are tricky there wrt to locking order.
1965 */
1966 PINTNETNETWORK pNetwork = pIf->pNetwork;
1967 if (!pNetwork)
1968 return VERR_WRONG_ORDER;
1969 return intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
1970}
1971
1972
1973/**
1974 * Sets the active property of an interface.
1975 *
1976 * @returns VBox status code.
1977 * @param pIntNet The instance handle.
1978 * @param hIf The interface handle.
1979 * @param pSession The caller's session.
1980 * @param fActive The new state.
1981 */
1982INTNETR0DECL(int) INTNETR0IfSetActive(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
1983{
1984 LogFlow(("INTNETR0IfSetActive: pIntNet=%p hIf=%RX32 fActive=%RTbool\n", pIntNet, hIf, fActive));
1985
1986 /*
1987 * Validate & translate input.
1988 */
1989 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
1990 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
1991 if (!pIf)
1992 {
1993 Log(("INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
1994 return VERR_INVALID_HANDLE;
1995 }
1996
1997 /*
1998 * Hand it to the network since it might involve the trunk
1999 * and things are tricky there wrt to locking order.
2000 */
2001 int rc;
2002 PINTNETNETWORK pNetwork = pIf->pNetwork;
2003 if (pNetwork)
2004 rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2005 else
2006 rc = VERR_WRONG_ORDER;
2007
2008 intnetR0IfRelease(pIf, pSession);
2009 return rc;
2010}
2011
2012
2013/**
2014 * VMMR0 request wrapper for INTNETR0IfSetActive.
2015 *
2016 * @returns see INTNETR0IfSetActive.
2017 * @param pIntNet The internal networking instance.
2018 * @param pSession The caller's session.
2019 * @param pReq The request packet.
2020 */
2021INTNETR0DECL(int) INTNETR0IfSetActiveReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
2022{
2023 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2024 return VERR_INVALID_PARAMETER;
2025 return INTNETR0IfSetActive(pIntNet, pReq->hIf, pSession, pReq->fActive);
2026}
2027
2028
2029/**
2030 * Wait for the interface to get signaled.
2031 * The interface will be signaled when is put into the receive buffer.
2032 *
2033 * @returns VBox status code.
2034 * @param pIntNet The instance handle.
2035 * @param hIf The interface handle.
2036 * @param pSession The caller's session.
2037 * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
2038 * used if indefinite wait is desired.
2039 */
2040INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
2041{
2042 Log4(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));
2043
2044 /*
2045 * Get and validate essential handles.
2046 */
2047 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2048 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2049 if (!pIf)
2050 {
2051 Log(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
2052 return VERR_INVALID_HANDLE;
2053 }
2054 const INTNETIFHANDLE hIfSelf = pIf->hIf;
2055 const RTSEMEVENT Event = pIf->Event;
2056 if ( hIfSelf != hIf /* paranoia */
2057 && Event != NIL_RTSEMEVENT)
2058 {
2059 Log(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
2060 return VERR_SEM_DESTROYED;
2061 }
2062
2063 /*
2064 * It is tempting to check if there is data to be read here,
2065 * but the problem with such an approach is that it will cause
2066 * one unnecessary supervisor->user->supervisor trip. There is
2067 * already a slight risk for such, so no need to increase it.
2068 */
2069
2070 /*
2071 * Increment the number of waiters before starting the wait.
2072 * Upon wakeup we must assert reality, checking that we're not
2073 * already destroyed or in the process of being destroyed. This
2074 * code must be aligned with the waiting code in intnetR0IfDestruct.
2075 */
2076 ASMAtomicIncU32(&pIf->cSleepers);
2077 int rc = RTSemEventWaitNoResume(Event, cMillies);
2078 if (pIf->Event == Event)
2079 {
2080 ASMAtomicDecU32(&pIf->cSleepers);
2081 if (!pIf->fDestroying)
2082 {
2083 intnetR0IfRelease(pIf, pSession);
2084 if (pIf->hIf != hIf)
2085 rc = VERR_SEM_DESTROYED;
2086 }
2087 else
2088 rc = VERR_SEM_DESTROYED;
2089 }
2090 else
2091 rc = VERR_SEM_DESTROYED;
2092 Log4(("INTNETR0IfWait: returns %Rrc\n", rc));
2093 return rc;
2094}
2095
2096
2097/**
2098 * VMMR0 request wrapper for INTNETR0IfWait.
2099 *
2100 * @returns see INTNETR0IfWait.
2101 * @param pIntNet The internal networking instance.
2102 * @param pSession The caller's session.
2103 * @param pReq The request packet.
2104 */
2105INTNETR0DECL(int) INTNETR0IfWaitReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
2106{
2107 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2108 return VERR_INVALID_PARAMETER;
2109 return INTNETR0IfWait(pIntNet, pReq->hIf, pSession, pReq->cMillies);
2110}
2111
2112
2113/**
2114 * Close an interface.
2115 *
2116 * @returns VBox status code.
2117 * @param pIntNet The instance handle.
2118 * @param hIf The interface handle.
2119 * @param pSession The caller's session.
2120 */
2121INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
2122{
2123 LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));
2124
2125 /*
2126 * Validate and free the handle.
2127 */
2128 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2129 PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
2130 if (!pIf)
2131 return VERR_INVALID_HANDLE;
2132
2133 /* mark the handle as freed so intnetR0IfDestruct won't free it again. */
2134 ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
2135
2136
2137 /*
2138 * Release the references to the interface object (handle + free lookup).
2139 * But signal the event semaphore first so any waiter holding a reference
2140 * will wake up too (he'll see hIf == invalid and return correctly).
2141 */
2142 RTSemEventSignal(pIf->Event);
2143
2144 void *pvObj = pIf->pvObj;
2145 intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
2146
2147 int rc = SUPR0ObjRelease(pvObj, pSession);
2148 LogFlow(("INTNETR0IfClose: returns %Rrc\n", rc));
2149 return rc;
2150}
2151
2152
2153/**
2154 * VMMR0 request wrapper for INTNETR0IfCloseReq.
2155 *
2156 * @returns see INTNETR0IfClose.
2157 * @param pIntNet The internal networking instance.
2158 * @param pSession The caller's session.
2159 * @param pReq The request packet.
2160 */
2161INTNETR0DECL(int) INTNETR0IfCloseReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
2162{
2163 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2164 return VERR_INVALID_PARAMETER;
2165 return INTNETR0IfClose(pIntNet, pReq->hIf, pSession);
2166}
2167
2168
2169/**
2170 * Interface destructor callback.
2171 * This is called for reference counted objectes when the count reaches 0.
2172 *
2173 * @param pvObj The object pointer.
2174 * @param pvUser1 Pointer to the interface.
2175 * @param pvUser2 Pointer to the INTNET instance data.
2176 */
2177static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
2178{
2179 PINTNETIF pIf = (PINTNETIF)pvUser1;
2180 PINTNET pIntNet = (PINTNET)pvUser2;
2181 Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
2182
2183 RTSemFastMutexRequest(pIntNet->FastMutex);
2184
2185 /*
2186 * Mark the interface as being destroyed so the waiter
2187 * can behave appropriately (theoretical case).
2188 */
2189 ASMAtomicWriteBool(&pIf->fDestroying, true);
2190
2191 /*
2192 * Delete the interface handle so the object no longer can be used.
2193 * (Can happen if the client didn't close its session.)
2194 */
2195 INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
2196 if (hIf != INTNET_HANDLE_INVALID)
2197 {
2198 void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession);
2199 AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
2200 }
2201
2202 /*
2203 * If we've got a network deactivate and unlink ourselves from it.
2204 * Because of cleanup order we might be an orphan now.
2205 */
2206 PINTNETNETWORK pNetwork = pIf->pNetwork;
2207 if (pNetwork)
2208 {
2209 intnetR0IfSetActive(pIf, false);
2210
2211 if (pNetwork->pIFs == pIf)
2212 pNetwork->pIFs = pIf->pNext;
2213 else
2214 {
2215 PINTNETIF pPrev = pNetwork->pIFs;
2216 while (pPrev)
2217 {
2218 if (pPrev->pNext == pIf)
2219 {
2220 pPrev->pNext = pIf->pNext;
2221 break;
2222 }
2223 pPrev = pPrev->pNext;
2224 }
2225 Assert(pPrev);
2226 }
2227 pIf->pNext = NULL;
2228
2229 /*
2230 * Release our reference to the network.
2231 */
2232 RTSemFastMutexRelease(pIntNet->FastMutex);
2233
2234 SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
2235 pIf->pNetwork = NULL;
2236 }
2237 else
2238 RTSemFastMutexRelease(pIntNet->FastMutex);
2239
2240 /*
2241 * Wakeup anyone waiting on this interface.
2242 *
2243 * We *must* make sure they have woken up properly and realized
2244 * that the interface is no longer valid.
2245 */
2246 if (pIf->Event != NIL_RTSEMEVENT)
2247 {
2248 RTSEMEVENT Event = pIf->Event;
2249 unsigned cMaxWait = 0x1000;
2250 while (pIf->cSleepers && cMaxWait-- > 0)
2251 {
2252 RTSemEventSignal(Event);
2253 RTThreadYield();
2254 }
2255 if (pIf->cSleepers)
2256 {
2257 RTThreadSleep(1);
2258
2259 cMaxWait = pIf->cSleepers;
2260 while (pIf->cSleepers && cMaxWait-- > 0)
2261 {
2262 RTSemEventSignal(Event);
2263 RTThreadSleep(10);
2264 }
2265 }
2266
2267 RTSemEventDestroy(Event);
2268 pIf->Event = NIL_RTSEMEVENT;
2269 }
2270
2271 /*
2272 * Unmap user buffer.
2273 */
2274 if (pIf->pIntBuf != pIf->pIntBufDefault)
2275 {
2276 /** @todo user buffer */
2277 }
2278
2279 /*
2280 * Unmap and Free the default buffer.
2281 */
2282 if (pIf->pIntBufDefault)
2283 {
2284 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
2285 pIf->pIntBufDefault = NULL;
2286 pIf->pIntBufDefaultR3 = 0;
2287 pIf->pIntBuf = NULL;
2288 pIf->pIntBufR3 = 0;
2289 }
2290
2291 /*
2292 * The interface.
2293 */
2294 pIf->pvObj = NULL;
2295 RTMemFree(pIf);
2296}
2297
2298
2299/**
2300 * Creates a new network interface.
2301 *
2302 * The call must have opened the network for the new interface
2303 * and is responsible for closing it on failure. On success
2304 * it must leave the network opened so the interface destructor
2305 * can close it.
2306 *
2307 * @returns VBox status code.
2308 * @param pNetwork The network.
2309 * @param pSession The session handle.
2310 * @param cbSend The size of the send buffer.
2311 * @param cbRecv The size of the receive buffer.
2312 * @param phIf Where to store the interface handle.
2313 */
2314static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, bool *pfCloseNetwork, PINTNETIFHANDLE phIf)
2315{
2316 LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
2317 pNetwork, pSession, cbSend, cbRecv, phIf));
2318
2319 /*
2320 * Assert input.
2321 */
2322 AssertPtr(pNetwork);
2323 AssertPtr(phIf);
2324 AssertPtr(pfCloseNetwork);
2325 *pfCloseNetwork = false;
2326
2327 /*
2328 * Allocate and initialize the interface structure.
2329 */
2330 PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
2331 if (!pIf)
2332 return VERR_NO_MEMORY;
2333 //pIf->pNext = NULL;
2334 memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
2335 //pIf->fMacSet = false;
2336 //pIf->fPromiscuous = false;
2337 //pIf->fActive = false;
2338 //pIf->fDestroying = false;
2339 //pIf->pIntBuf = 0;
2340 //pIf->pIntBufR3 = NIL_RTR3PTR;
2341 //pIf->pIntBufDefault = 0;
2342 //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
2343 //pIf->cYields = 0;
2344 pIf->Event = NIL_RTSEMEVENT;
2345 //pIf->cSleepers = 0;
2346 pIf->hIf = INTNET_HANDLE_INVALID;
2347 pIf->pNetwork = pNetwork;
2348 pIf->pSession = pSession;
2349 //pIf->pvObj = NULL;
2350 int rc = RTSemEventCreate((PRTSEMEVENT)&pIf->Event);
2351 if (RT_SUCCESS(rc))
2352 {
2353 /*
2354 * Create the default buffer.
2355 */
2356 /** @todo adjust with minimums and apply defaults here. */
2357 cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
2358 cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
2359 const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
2360 rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
2361 if (RT_SUCCESS(rc))
2362 {
2363 ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
2364
2365 pIf->pIntBuf = pIf->pIntBufDefault;
2366 pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
2367 pIf->pIntBuf->cbBuf = cbBuf;
2368 pIf->pIntBuf->cbRecv = cbRecv;
2369 pIf->pIntBuf->cbSend = cbSend;
2370 /* receive ring buffer. */
2371 pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
2372 pIf->pIntBuf->Recv.offRead = pIf->pIntBuf->Recv.offStart;
2373 pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
2374 pIf->pIntBuf->Recv.offEnd = pIf->pIntBuf->Recv.offStart + cbRecv;
2375 /* send ring buffer. */
2376 pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
2377 pIf->pIntBuf->Send.offRead = pIf->pIntBuf->Send.offStart;
2378 pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
2379 pIf->pIntBuf->Send.offEnd = pIf->pIntBuf->Send.offStart + cbSend;
2380
2381 /*
2382 * Link the interface to the network.
2383 */
2384 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2385 if (RT_SUCCESS(rc))
2386 {
2387 pIf->pNext = pNetwork->pIFs;
2388 pNetwork->pIFs = pIf;
2389 RTSemFastMutexRelease(pNetwork->FastMutex);
2390
2391 /*
2392 * Register the interface with the session.
2393 */
2394 pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, intnetR0IfDestruct, pIf, pNetwork->pIntNet);
2395 if (pIf->pvObj)
2396 {
2397 rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
2398 if (RT_SUCCESS(rc))
2399 {
2400 *phIf = pIf->hIf;
2401 Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
2402 *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
2403 return VINF_SUCCESS;
2404 }
2405
2406 SUPR0ObjRelease(pIf->pvObj, pSession);
2407 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
2408 return rc;
2409 }
2410
2411 RTSemFastMutexDestroy(pNetwork->FastMutex);
2412 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
2413 }
2414
2415 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
2416 pIf->pIntBufDefault = NULL;
2417 pIf->pIntBuf = NULL;
2418 }
2419
2420 RTSemEventDestroy(pIf->Event);
2421 pIf->Event = NIL_RTSEMEVENT;
2422 }
2423 RTMemFree(pIf);
2424 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
2425 *pfCloseNetwork = true;
2426 return rc;
2427}
2428
2429
2430
2431
2432
2433/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
2434static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
2435{
2436 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
2437 AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
2438 return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
2439}
2440
2441
2442/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
2443static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
2444{
2445 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
2446 PINTNETNETWORK pNetwork = pThis->pNetwork;
2447
2448 /* assert some sanity */
2449 AssertPtrReturn(pNetwork, false);
2450 AssertReturn(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX, false);
2451 AssertPtr(pSG);
2452 Assert(fSrc);
2453
2454 /*
2455 * Lock the network and send the frame to it.
2456 */
2457 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2458 AssertRCReturn(rc, false);
2459
2460 bool fRc;
2461 if (RT_LIKELY(pNetwork->cActiveIFs > 0))
2462 fRc = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, false /* fTrunkLocked */);
2463 else
2464 fRc = false; /* don't drop it */
2465
2466 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2467 AssertRC(rc);
2468
2469 return fRc;
2470}
2471
2472
2473/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
2474static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
2475{
2476 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
2477 PINTNETNETWORK pNetwork = pThis->pNetwork;
2478
2479 /* assert some sanity */
2480 AssertPtrReturnVoid(pNetwork);
2481 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
2482 AssertPtr(pSG);
2483 Assert(pSG->cUsers > 0);
2484
2485 /* do it. */
2486 ++pSG->cUsers;
2487}
2488
2489
2490/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
2491static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
2492{
2493 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
2494 PINTNETNETWORK pNetwork = pThis->pNetwork;
2495
2496 /* assert some sanity */
2497 AssertPtrReturnVoid(pNetwork);
2498 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
2499 AssertPtr(pSG);
2500 Assert(pSG->cUsers > 0);
2501
2502 /*
2503 * Free it?
2504 */
2505 if (!--pSG->cUsers)
2506 {
2507 /** @todo later */
2508 }
2509}
2510
2511
2512/**
2513 * Retain the trunk interface.
2514 *
2515 * @returns pThis if retained.
2516 *
2517 * @param pThis The trunk.
2518 *
2519 * @remarks Any locks.
2520 */
2521static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
2522{
2523 if (pThis && pThis->pIfPort)
2524 {
2525 pThis->pIfPort->pfnRetain(pThis->pIfPort);
2526 return pThis;
2527 }
2528 return NULL;
2529}
2530
2531
2532/**
2533 * Release the trunk interface.
2534 *
2535 * @param pThis The trunk.
2536 */
2537static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
2538{
2539 if (pThis && pThis->pIfPort)
2540 pThis->pIfPort->pfnRelease(pThis->pIfPort);
2541}
2542
2543
2544/**
2545 * Takes the out-bound trunk lock.
2546 *
2547 * This will ensure that pIfPort is valid.
2548 *
2549 * @returns success indicator.
2550 * @param pThis The trunk.
2551 *
2552 * @remarks No locks other than the create/destroy one.
2553 */
2554static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis)
2555{
2556 AssertPtrReturn(pThis, false);
2557 int rc = RTSemFastMutexRequest(pThis->FastMutex);
2558 if (RT_SUCCESS(rc))
2559 {
2560 if (RT_LIKELY(pThis->pIfPort))
2561 return true;
2562 RTSemFastMutexRelease(pThis->FastMutex);
2563 }
2564 else
2565 AssertMsg(rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc));
2566 return false;
2567}
2568
2569
2570/**
2571 * Releases the out-bound trunk lock.
2572 *
2573 * @param pThis The trunk.
2574 */
2575static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis)
2576{
2577 if (pThis)
2578 {
2579 int rc = RTSemFastMutexRelease(pThis->FastMutex);
2580 AssertRC(rc);
2581 }
2582}
2583
2584
2585/**
2586 * Activates the trunk interface.
2587 *
2588 * @param pThis The trunk.
2589 * @param fActive What to do with it.
2590 *
2591 * @remarks Caller may only own the create/destroy lock.
2592 */
2593static void intnetR0TrunkIfActivate(PINTNETTRUNKIF pThis, bool fActive)
2594{
2595 if (intnetR0TrunkIfOutLock(pThis))
2596 {
2597 pThis->pIfPort->pfnSetActive(pThis->pIfPort, fActive);
2598 intnetR0TrunkIfOutUnlock(pThis);
2599 }
2600}
2601
2602
2603/**
2604 * Shutdown the trunk interface.
2605 *
2606 * @param pThis The trunk.
2607 * @param pNetworks The network.
2608 *
2609 * @remarks The caller must *NOT* hold the network lock. The global
2610 * create/destroy lock is fine though.
2611 */
2612static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
2613{
2614 /* assert sanity */
2615 if (!pThis)
2616 return;
2617 AssertPtr(pThis);
2618 Assert(pThis->pNetwork == pNetwork);
2619 AssertPtrNull(pThis->pIfPort);
2620
2621 /*
2622 * The interface has already been deactivated, we just to wait for
2623 * it to become idle before we can disconnect and release it.
2624 */
2625 PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
2626 if (pIfPort)
2627 {
2628 intnetR0TrunkIfOutLock(pThis);
2629
2630 /* unset it */
2631 pThis->pIfPort = NULL;
2632
2633 /* wait in portions so we can complain ever now an then. */
2634 uint64_t StartTS = RTTimeSystemNanoTS();
2635 int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
2636 if (RT_FAILURE(rc))
2637 {
2638 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
2639 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
2640 Assert(rc == VERR_TIMEOUT);
2641 while ( RT_FAILURE(rc)
2642 && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
2643 rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
2644 if (rc == VERR_TIMEOUT)
2645 {
2646 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
2647 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
2648 while ( rc == VERR_TIMEOUT
2649 && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
2650 rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
2651 if (RT_FAILURE(rc))
2652 {
2653 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
2654 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
2655 AssertRC(rc);
2656 }
2657 }
2658 }
2659
2660 /* disconnect & release it. */
2661 pIfPort->pfnDisconnectAndRelease(pIfPort);
2662 }
2663
2664 /*
2665 * Free up the resources.
2666 */
2667 RTSEMFASTMUTEX FastMutex = pThis->FastMutex;
2668 pThis->FastMutex = NIL_RTSEMFASTMUTEX;
2669 pThis->pNetwork = NULL;
2670 RTSemFastMutexRelease(FastMutex);
2671 RTSemFastMutexDestroy(FastMutex);
2672 RTMemFree(pThis);
2673}
2674
2675
2676/**
2677 * Creates the trunk connection (if any).
2678 *
2679 * @returns VBox status code.
2680 *
2681 * @param pNetwork The newly created network.
2682 * @param pSession The session handle.
2683 */
2684static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
2685{
2686 const char *pszName;
2687 switch (pNetwork->enmTrunkType)
2688 {
2689 /*
2690 * The 'None' case, simple.
2691 */
2692 case kIntNetTrunkType_None:
2693 case kIntNetTrunkType_WhateverNone:
2694 return VINF_SUCCESS;
2695
2696 /* Can't happen, but makes GCC happy. */
2697 default:
2698 return VERR_NOT_IMPLEMENTED;
2699
2700 /*
2701 * Translate enum to component factory name.
2702 */
2703 case kIntNetTrunkType_NetFlt:
2704 pszName = "VBoxNetFlt";
2705 break;
2706 case kIntNetTrunkType_NetTap:
2707 pszName = "VBoxNetTap";
2708 break;
2709 case kIntNetTrunkType_SrvNat:
2710 pszName = "VBoxSrvNat";
2711 break;
2712 }
2713
2714 /*
2715 * Allocate the trunk interface.
2716 */
2717 PINTNETTRUNKIF pTrunkIF = (PINTNETTRUNKIF)RTMemAllocZ(sizeof(*pTrunkIF));
2718 if (!pTrunkIF)
2719 return VERR_NO_MEMORY;
2720 pTrunkIF->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
2721 pTrunkIF->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
2722 pTrunkIF->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
2723 pTrunkIF->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
2724 pTrunkIF->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
2725 pTrunkIF->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
2726 //pTrunkIF->pIfPort = NULL;
2727 pTrunkIF->pNetwork = pNetwork;
2728 //pTrunkIF->fPhysSG = false;
2729 //pTrunkIF->fPromiscuousWire = false;
2730 int rc = RTSemFastMutexCreate(&pTrunkIF->FastMutex);
2731 if (RT_SUCCESS(rc))
2732 {
2733#ifdef IN_RING0 /* (testcase is ring-3) */
2734 /*
2735 * Query the factory we want, then use it create and connect the trunk.
2736 */
2737 PINTNETTRUNKFACTORY pTrunkFactory = NULL;
2738 rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
2739 if (RT_SUCCESS(rc))
2740 {
2741 rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory, pNetwork->szTrunk, &pTrunkIF->SwitchPort, &pTrunkIF->pIfPort);
2742 pTrunkFactory->pfnRelease(pTrunkFactory);
2743 if (RT_SUCCESS(rc))
2744 {
2745 Assert(pTrunkIF->pIfPort);
2746 pNetwork->pTrunkIF = pTrunkIF;
2747 LogFlow(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s Network=%s\n",
2748 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
2749 return VINF_SUCCESS;
2750 }
2751 }
2752#endif /* IN_RING0 */
2753 RTSemFastMutexDestroy(pTrunkIF->FastMutex);
2754 }
2755 RTMemFree(pTrunkIF);
2756 LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
2757 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
2758 return rc;
2759}
2760
2761
2762
2763/**
2764 * Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork().
2765 *
2766 * @param pNetwork The network to close.
2767 * @param pSession The session handle.
2768 */
2769static int intnetR0NetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
2770{
2771 LogFlow(("intnetR0NetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
2772 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
2773 AssertPtrReturn(pNetwork, VERR_INVALID_PARAMETER);
2774
2775 int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
2776 LogFlow(("intnetR0NetworkClose: return %Rrc\n", rc));
2777 return rc;
2778}
2779
2780
2781/**
2782 * Object destructor callback.
2783 * This is called for reference counted objectes when the count reaches 0.
2784 *
2785 * @param pvObj The object pointer.
2786 * @param pvUser1 Pointer to the network.
2787 * @param pvUser2 Pointer to the INTNET instance data.
2788 */
2789static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
2790{
2791 PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
2792 PINTNET pIntNet = (PINTNET)pvUser2;
2793 Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
2794 Assert(pNetwork->pIntNet == pIntNet);
2795
2796 /* take the create/destroy sem. */
2797 RTSemFastMutexRequest(pIntNet->FastMutex);
2798
2799 /*
2800 * Deactivate the trunk connection first (if any).
2801 */
2802 if (pNetwork->pTrunkIF)
2803 intnetR0TrunkIfActivate(pNetwork->pTrunkIF, false /* fActive */);
2804
2805 /*
2806 * Unlink the network.
2807 * Note that it needn't be in the list if we failed during creation.
2808 */
2809 PINTNETNETWORK pPrev = pIntNet->pNetworks;
2810 if (pPrev == pNetwork)
2811 pIntNet->pNetworks = pNetwork->pNext;
2812 else
2813 {
2814 for (; pPrev; pPrev = pPrev->pNext)
2815 if (pPrev->pNext == pNetwork)
2816 {
2817 pPrev->pNext = pNetwork->pNext;
2818 break;
2819 }
2820 }
2821 pNetwork->pNext = NULL;
2822 pNetwork->pvObj = NULL;
2823
2824 /*
2825 * Because of the undefined order of the per session object dereferencing when closing a session,
2826 * we have to handle the case where the network is destroyed before the interfaces. We'll
2827 * deal with this by simply orphaning the interfaces.
2828 */
2829 RTSemFastMutexRequest(pNetwork->FastMutex);
2830
2831 PINTNETIF pCur = pNetwork->pIFs;
2832 while (pCur)
2833 {
2834 PINTNETIF pNext = pCur->pNext;
2835 pCur->pNext = NULL;
2836 pCur->pNetwork = NULL;
2837 pCur = pNext;
2838 }
2839
2840 /* Grab and zap the trunk pointer before leaving the mutex. */
2841 PINTNETTRUNKIF pTrunkIF = pNetwork->pTrunkIF;
2842 pNetwork->pTrunkIF = NULL;
2843
2844 RTSemFastMutexRelease(pNetwork->FastMutex);
2845
2846 /*
2847 * If there is a trunk, delete it.
2848 * Note that this may tak a while if we're unlucky...
2849 */
2850 if (pTrunkIF)
2851 intnetR0TrunkIfDestroy(pTrunkIF, pNetwork);
2852
2853 /*
2854 * Free resources.
2855 */
2856 RTSemFastMutexDestroy(pNetwork->FastMutex);
2857 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
2858 RTMemFree(pNetwork);
2859
2860 /* release the create/destroy sem. (can be done before trunk destruction.) */
2861 RTSemFastMutexRelease(pIntNet->FastMutex);
2862}
2863
2864
2865/**
2866 * Opens an existing network.
2867 *
2868 * @returns VBox status code.
2869 * @param pIntNet The instance data.
2870 * @param pSession The current session.
2871 * @param pszNetwork The network name. This has a valid length.
2872 * @param enmTrunkType The trunk type.
2873 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
2874 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
2875 * @param ppNetwork Where to store the pointer to the network on success.
2876 */
2877static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
2878 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
2879{
2880 LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
2881 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
2882
2883 /* just pro forma validation, the caller is internal. */
2884 AssertPtr(pIntNet);
2885 AssertPtr(pSession);
2886 AssertPtr(pszNetwork);
2887 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
2888 AssertPtr(pszTrunk);
2889 Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
2890 AssertPtr(ppNetwork);
2891 *ppNetwork = NULL;
2892
2893 /*
2894 * Search networks by name.
2895 */
2896 PINTNETNETWORK pCur;
2897 uint8_t cchName = strlen(pszNetwork);
2898 Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
2899
2900 pCur = pIntNet->pNetworks;
2901 while (pCur)
2902 {
2903 if ( pCur->cchName == cchName
2904 && !memcmp(pCur->szName, pszNetwork, cchName))
2905 {
2906 /*
2907 * Found the network, now check that we have the same ideas
2908 * about the trunk setup and security.
2909 */
2910 int rc;
2911 if ( enmTrunkType == kIntNetTrunkType_WhateverNone
2912 || ( pCur->enmTrunkType == enmTrunkType
2913 && !strcmp(pCur->szTrunk, pszTrunk)))
2914 {
2915 if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
2916 {
2917
2918 /*
2919 * Increment the reference and check that the session
2920 * can access this network.
2921 */
2922 rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
2923 if (RT_SUCCESS(rc))
2924 {
2925 if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
2926 rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
2927 if (RT_SUCCESS(rc))
2928 {
2929 pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
2930
2931 *ppNetwork = pCur;
2932 }
2933 else
2934 SUPR0ObjRelease(pCur->pvObj, pSession);
2935 }
2936 else if (rc == VERR_WRONG_ORDER)
2937 rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
2938 }
2939 else
2940 rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
2941 }
2942 else
2943 rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
2944
2945 LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
2946 return rc;
2947 }
2948 pCur = pCur->pNext;
2949 }
2950
2951 LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
2952 return VERR_NOT_FOUND;
2953}
2954
2955
2956/**
2957 * Creates a new network.
2958 *
2959 * The call must own the INTNET::FastMutex and has already attempted
2960 * opening the network and found it to be non-existing.
2961 *
2962 * @returns VBox status code.
2963 * @param pIntNet The instance data.
2964 * @param pSession The session handle.
2965 * @param pszNetwork The name of the network. This must be at least one character long and no longer
2966 * than the INTNETNETWORK::szName.
2967 * @param enmTrunkType The trunk type.
2968 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
2969 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
2970 * @param ppNetwork Where to store the network. In the case of failure whatever is returned
2971 * here should be dereferenced outside the INTNET::FastMutex.
2972 */
2973static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
2974 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
2975{
2976 LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
2977 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
2978
2979 /* just pro forma validation, the caller is internal. */
2980 AssertPtr(pIntNet);
2981 AssertPtr(pSession);
2982 AssertPtr(pszNetwork);
2983 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
2984 AssertPtr(pszTrunk);
2985 Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
2986 AssertPtr(ppNetwork);
2987 *ppNetwork = NULL;
2988
2989 /*
2990 * Allocate and initialize.
2991 */
2992 PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(sizeof(*pNew));
2993 if (!pNew)
2994 return VERR_NO_MEMORY;
2995 int rc = RTSemFastMutexCreate(&pNew->FastMutex);
2996 if (RT_SUCCESS(rc))
2997 {
2998 //pNew->pIFs = NULL;
2999 pNew->pIntNet = pIntNet;
3000 //pNew->cActiveIFs = 0;
3001 pNew->fFlags = fFlags;
3002 size_t cchName = strlen(pszNetwork);
3003 pNew->cchName = cchName;
3004 Assert(cchName && cchName < sizeof(pNew->szName)); /* caller's responsibility. */
3005 memcpy(pNew->szName, pszNetwork, cchName); /* '\0' by alloc. */
3006 pNew->enmTrunkType = enmTrunkType;
3007 Assert(strlen(pszTrunk) < sizeof(pNew->szTrunk)); /* caller's responsibility. */
3008 strcpy(pNew->szTrunk, pszTrunk);
3009
3010 /*
3011 * Register the object in the current session and link it into the network list.
3012 */
3013 pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNew, pIntNet);
3014 if (pNew->pvObj)
3015 {
3016 pNew->pNext = pIntNet->pNetworks;
3017 pIntNet->pNetworks = pNew;
3018
3019 /*
3020 * Check if the current session is actually allowed to create and open
3021 * the network. It is possible to implement network name based policies
3022 * and these must be checked now. SUPR0ObjRegister does no such checks.
3023 */
3024 rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
3025 if (RT_SUCCESS(rc))
3026 {
3027 /*
3028 * Connect the trunk.
3029 */
3030 rc = intnetR0NetworkCreateTrunkIf(pNew, pSession);
3031 if (RT_SUCCESS(rc))
3032 {
3033 *ppNetwork = pNew;
3034 LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
3035 return VINF_SUCCESS;
3036 }
3037 }
3038
3039 /*
3040 * We unlink it here so it cannot be opened when the caller leaves
3041 * INTNET::FastMutex before dereferencing it.
3042 */
3043 Assert(pIntNet->pNetworks == pNew);
3044 pIntNet->pNetworks = pNew->pNext;
3045 pNew->pNext = NULL;
3046
3047 *ppNetwork = pNew;
3048 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3049 return rc;
3050 }
3051 rc = VERR_NO_MEMORY;
3052
3053 RTSemFastMutexDestroy(pNew->FastMutex);
3054 pNew->FastMutex = NIL_RTSEMFASTMUTEX;
3055 }
3056 RTMemFree(pNew);
3057 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3058 return rc;
3059}
3060
3061
3062/**
3063 * Opens a network interface and connects it to the specified network.
3064 *
3065 * @returns VBox status code.
3066 * @param pIntNet The internal network instance.
3067 * @param pSession The session handle.
3068 * @param pszNetwork The network name.
3069 * @param enmTrunkType The trunk type.
3070 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3071 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3072 * @param fRestrictAccess Whether new participants should be subjected to access check or not.
3073 * @param cbSend The send buffer size.
3074 * @param cbRecv The receive buffer size.
3075 * @param phIf Where to store the handle to the network interface.
3076 */
3077INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork,
3078 INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
3079 unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
3080{
3081 LogFlow(("INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
3082 pIntNet, pSession, pszNetwork, pszNetwork, pszTrunk, pszTrunk, enmTrunkType, fFlags, cbSend, cbRecv, phIf));
3083
3084 /*
3085 * Validate input.
3086 */
3087 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3088
3089 AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
3090 const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
3091 AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
3092 size_t cchNetwork = pszNetworkEnd - pszNetwork;
3093 AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
3094
3095 if (pszTrunk)
3096 {
3097 AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
3098 const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
3099 AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
3100 }
3101 else
3102 pszTrunk = "";
3103
3104 AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
3105 ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
3106 switch (enmTrunkType)
3107 {
3108 case kIntNetTrunkType_None:
3109 case kIntNetTrunkType_WhateverNone:
3110 AssertReturn(!*pszTrunk, VERR_INVALID_PARAMETER);
3111 break;
3112
3113 case kIntNetTrunkType_NetFlt:
3114 AssertReturn(pszTrunk, VERR_INVALID_PARAMETER);
3115 break;
3116
3117 default:
3118 return VERR_NOT_IMPLEMENTED;
3119 }
3120
3121 AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
3122 AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
3123
3124 /*
3125 * Acquire the mutex to serialize open/create.
3126 */
3127 int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
3128 if (RT_FAILURE(rc))
3129 return rc;
3130
3131 /*
3132 * Try open / create the network and create an interface on it for the caller to use.
3133 *
3134 * Note that because of the destructors grabbing INTNET::FastMutex and us being required
3135 * to own this semaphore for the entire network opening / creation and interface creation
3136 * sequence, intnetR0CreateNetwork will have to defer the network cleanup to us on failure.
3137 */
3138 PINTNETNETWORK pNetwork = NULL;
3139 rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
3140 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
3141 {
3142 bool fCloseNetwork = true;
3143 if (rc == VERR_NOT_FOUND)
3144 rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
3145 if (RT_SUCCESS(rc))
3146 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, &fCloseNetwork, phIf);
3147
3148 RTSemFastMutexRelease(pIntNet->FastMutex);
3149
3150 if (RT_FAILURE(rc) && pNetwork && fCloseNetwork)
3151 intnetR0NetworkClose(pNetwork, pSession);
3152 }
3153 else
3154 RTSemFastMutexRelease(pIntNet->FastMutex);
3155
3156 LogFlow(("INTNETR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
3157 return rc;
3158}
3159
3160
3161/**
3162 * VMMR0 request wrapper for GMMR0MapUnmapChunk.
3163 *
3164 * @returns see GMMR0MapUnmapChunk.
3165 * @param pIntNet The internal networking instance.
3166 * @param pSession The caller's session.
3167 * @param pReq The request packet.
3168 */
3169INTNETR0DECL(int) INTNETR0OpenReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
3170{
3171 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3172 return VERR_INVALID_PARAMETER;
3173 return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
3174 pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
3175}
3176
3177
3178/**
3179 * Destroys an instance of the Ring-0 internal networking service.
3180 *
3181 * @param pIntNet Pointer to the instance data.
3182 */
3183INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
3184{
3185 LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));
3186
3187 /*
3188 * Allow NULL pointers.
3189 */
3190 if (!pIntNet)
3191 return;
3192 AssertPtrReturnVoid(pIntNet);
3193
3194 /*
3195 * There is not supposed to be any networks hanging around at this time.
3196 */
3197 Assert(pIntNet->pNetworks == NULL);
3198 if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
3199 {
3200 RTSemFastMutexDestroy(pIntNet->FastMutex);
3201 pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
3202 }
3203 if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
3204 {
3205 /** @todo does it make sense to have a deleter here? */
3206 RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
3207 pIntNet->hHtIfs = NIL_RTHANDLETABLE;
3208 }
3209
3210 RTMemFree(pIntNet);
3211}
3212
3213
3214/**
3215 * Create an instance of the Ring-0 internal networking service.
3216 *
3217 * @returns VBox status code.
3218 * @param ppIntNet Where to store the instance pointer.
3219 */
3220INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
3221{
3222 LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
3223 int rc = VERR_NO_MEMORY;
3224 PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
3225 if (pIntNet)
3226 {
3227 //pIntNet->pNetworks = NULL;
3228
3229 rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
3230 if (RT_SUCCESS(rc))
3231 {
3232 rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
3233 UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
3234 if (RT_SUCCESS(rc))
3235 {
3236 *ppIntNet = pIntNet;
3237 LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
3238 return VINF_SUCCESS;
3239 }
3240
3241 RTSemFastMutexDestroy(pIntNet->FastMutex);
3242 }
3243 RTMemFree(pIntNet);
3244 }
3245 *ppIntNet = NULL;
3246 LogFlow(("INTNETR0Create: returns %Rrc\n", rc));
3247 return rc;
3248}
3249
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