VirtualBox

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

最後變更 在這個檔案從29662是 29662,由 vboxsync 提交於 15 年 前

IntNet: Added Interface, Interface private data passing for per-interface based VBoxNetFlt.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 192.1 KB
 
1/* $Id: SrvIntNetR0.cpp 29662 2010-05-19 14:46:02Z vboxsync $ */
2/** @file
3 * Internal networking - The ring 0 service.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_SRV_INTNET
23#include <VBox/intnet.h>
24#include <VBox/intnetinline.h>
25#include <VBox/pdmnetinline.h>
26#include <VBox/sup.h>
27#include <VBox/pdm.h>
28#include <VBox/log.h>
29
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/handletable.h>
33#include <iprt/mp.h>
34#include <iprt/mem.h>
35#include <iprt/net.h>
36#include <iprt/semaphore.h>
37#include <iprt/spinlock.h>
38#include <iprt/string.h>
39#include <iprt/thread.h>
40#include <iprt/time.h>
41
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** @def INTNET_WITH_DHCP_SNOOPING
47 * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
48#define INTNET_WITH_DHCP_SNOOPING
49
50/** The maximum number of interface in a network. */
51#define INTNET_MAX_IFS (1023 + 1 + 16)
52
53/** The number of entries to grow the destination tables with. */
54#if 0
55# define INTNET_GROW_DSTTAB_SIZE 16
56#else
57# define INTNET_GROW_DSTTAB_SIZE 1
58#endif
59
60/** The wakeup bit in the INTNETIF::cBusy and INTNETRUNKIF::cBusy counters. */
61#define INTNET_BUSY_WAKEUP_MASK RT_BIT_32(30)
62
63
64/*******************************************************************************
65* Structures and Typedefs *
66*******************************************************************************/
67/**
68 * MAC address lookup table entry.
69 */
70typedef struct INTNETMACTABENTRY
71{
72 /** The MAC address of this entry. */
73 RTMAC MacAddr;
74 /** Is it promiscuous. */
75 bool fPromiscuous;
76 /** Is it active.
77 * We ignore the entry if this is clear and may end up sending packets addressed
78 * to this interface onto the trunk. The reasoning for this is that this could
79 * be the interface of a VM that just has been teleported to a different host. */
80 bool fActive;
81 /** Pointer to the network interface. */
82 struct INTNETIF *pIf;
83} INTNETMACTABENTRY;
84/** Pointer to a MAC address lookup table entry. */
85typedef INTNETMACTABENTRY *PINTNETMACTABENTRY;
86
87/**
88 * MAC address lookup table.
89 *
90 * @todo Having this in a separate structure didn't work out as well as it
91 * should. Consider merging it into INTNETNETWORK.
92 */
93typedef struct INTNETMACTAB
94{
95 /** The current number of entries. */
96 uint32_t cEntries;
97 /** The number of entries we've allocated space for. */
98 uint32_t cEntriesAllocated;
99 /** Table entries. */
100 PINTNETMACTABENTRY paEntries;
101
102 /** The host MAC address (reported). */
103 RTMAC HostMac;
104 /** The host promisucous setting (reported). */
105 bool fHostPromiscuous;
106 /** Whether the host is active. */
107 bool fHostActive;
108
109 /** Whether the wire is promiscuous (config). */
110 bool fWirePromiscuous;
111 /** Whether the wire is active. */
112 bool fWireActive;
113
114 /** Pointer to the the trunk interface. */
115 struct INTNETTRUNKIF *pTrunk;
116} INTNETMACTAB;
117/** Pointer to a MAC address . */
118typedef INTNETMACTAB *PINTNETMACTAB;
119
120/**
121 * Destination table.
122 */
123typedef struct INTNETDSTTAB
124{
125 /** The trunk destinations. */
126 uint32_t fTrunkDst;
127 /** Pointer to the trunk interface (referenced) if fTrunkDst is non-zero. */
128 struct INTNETTRUNKIF *pTrunk;
129 /** The number of destination interfaces. */
130 uint32_t cIfs;
131 /** The interfaces (referenced). Variable sized array. */
132 struct
133 {
134 /** The destination interface. */
135 struct INTNETIF *pIf;
136 /** Whether to replace the destination MAC address.
137 * This is used when sharing MAC address with the host on the wire(less). */
138 bool fReplaceDstMac;
139 } aIfs[1];
140} INTNETDSTTAB;
141/** Pointer to a destination table. */
142typedef INTNETDSTTAB *PINTNETDSTTAB;
143/** Pointer to a const destination table. */
144typedef INTNETDSTTAB const *PCINTNETDSTTAB;
145
146
147/** Network layer address type. */
148typedef enum INTNETADDRTYPE
149{
150 /** The invalid 0 entry. */
151 kIntNetAddrType_Invalid = 0,
152 /** IP version 4. */
153 kIntNetAddrType_IPv4,
154 /** IP version 6. */
155 kIntNetAddrType_IPv6,
156 /** IPX. */
157 kIntNetAddrType_IPX,
158 /** The end of the valid values. */
159 kIntNetAddrType_End,
160 /** The usual 32-bit hack. */
161 kIntNetAddrType_32BitHack = 0x7fffffff
162} INTNETADDRTYPE;
163/** Pointer to a network layer address type. */
164typedef INTNETADDRTYPE *PINTNETADDRTYPE;
165
166
167/**
168 * Address and type.
169 */
170typedef struct INTNETADDR
171{
172 /** The address type. */
173 INTNETADDRTYPE enmType;
174 /** The address. */
175 RTNETADDRU Addr;
176} INTNETADDR;
177/** Pointer to an address. */
178typedef INTNETADDR *PINTNETADDR;
179/** Pointer to a const address. */
180typedef INTNETADDR const *PCINTNETADDR;
181
182
183/**
184 * Address cache for a specific network layer.
185 */
186typedef struct INTNETADDRCACHE
187{
188 /** Pointer to the table of addresses. */
189 uint8_t *pbEntries;
190 /** The number of valid address entries. */
191 uint8_t cEntries;
192 /** The number of allocated address entries. */
193 uint8_t cEntriesAlloc;
194 /** The address size. */
195 uint8_t cbAddress;
196 /** The size of an entry. */
197 uint8_t cbEntry;
198} INTNETADDRCACHE;
199/** Pointer to an address cache. */
200typedef INTNETADDRCACHE *PINTNETADDRCACHE;
201/** Pointer to a const address cache. */
202typedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
203
204
205/**
206 * A network interface.
207 *
208 * Unless explicitly stated, all members are protect by the network semaphore.
209 */
210typedef struct INTNETIF
211{
212 /** The MAC address.
213 * This is shadowed by INTNETMACTABENTRY::MacAddr. */
214 RTMAC MacAddr;
215 /** Set if the INTNET::MacAddr member has been explicitly set. */
216 bool fMacSet;
217 /** Set if the interface is in promiscuous mode.
218 * This is shadowed by INTNETMACTABENTRY::fPromiscuous. */
219 bool fPromiscuous;
220 /** Whether the interface is active or not.
221 * This is shadowed by INTNETMACTABENTRY::fActive. */
222 bool fActive;
223 /** Whether someone is currently in the destructor. */
224 bool volatile fDestroying;
225 /** Number of yields done to try make the interface read pending data.
226 * We will stop yielding when this reaches a threshold assuming that the VM is
227 * paused or that it simply isn't worth all the delay. It is cleared when a
228 * successful send has been done. */
229 uint32_t cYields;
230 /** Pointer to the current exchange buffer (ring-0). */
231 PINTNETBUF pIntBuf;
232 /** Pointer to ring-3 mapping of the current exchange buffer. */
233 R3PTRTYPE(PINTNETBUF) pIntBufR3;
234 /** Pointer to the default exchange buffer for the interface. */
235 PINTNETBUF pIntBufDefault;
236 /** Pointer to ring-3 mapping of the default exchange buffer. */
237 R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
238 /** Event semaphore which a receiver/consumer thread will sleep on while
239 * waiting for data to arrive. */
240 RTSEMEVENT volatile hRecvEvent;
241 /** Number of threads sleeping on the event semaphore. */
242 uint32_t cSleepers;
243 /** The interface handle.
244 * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
245 * should return with the appropriate error condition. */
246 INTNETIFHANDLE volatile hIf;
247 /** Pointer to the network this interface is connected to.
248 * This is protected by the INTNET::hMtxCreateOpenDestroy. */
249 struct INTNETNETWORK *pNetwork;
250 /** The session this interface is associated with. */
251 PSUPDRVSESSION pSession;
252 /** The SUPR0 object id. */
253 void *pvObj;
254 /** The network layer address cache. (Indexed by type, 0 entry isn't used.)
255 * This is protected by the address spinlock of the network. */
256 INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
257 /** Spinlock protecting the input (producer) side of the receive ring. */
258 RTSPINLOCK hRecvInSpinlock;
259 /** Busy count for tracking destination table references and active sends.
260 * Usually incremented while owning the switch table spinlock. The 30th bit
261 * is used to indicate wakeup. */
262 uint32_t volatile cBusy;
263 /** The preallocated destination table.
264 * This is NULL when it's in use as a precaution against unserialized
265 * transmitting. This is grown when new interfaces are added to the network. */
266 PINTNETDSTTAB volatile pDstTab;
267 /** Pointer to the trunk's per interface data. Can be NULL. */
268 void *pvIfData;
269} INTNETIF;
270/** Pointer to an internal network interface. */
271typedef INTNETIF *PINTNETIF;
272
273
274/**
275 * A trunk interface.
276 */
277typedef struct INTNETTRUNKIF
278{
279 /** The port interface we present to the component. */
280 INTNETTRUNKSWPORT SwitchPort;
281 /** The port interface we get from the component. */
282 PINTNETTRUNKIFPORT pIfPort;
283 /** Pointer to the network we're connect to.
284 * This may be NULL if we're orphaned? */
285 struct INTNETNETWORK *pNetwork;
286 /** The current MAC address for the interface. (reported)
287 * Updated while owning the switch table spinlock. */
288 RTMAC MacAddr;
289 /** Whether to supply physical addresses with the outbound SGs. (reported) */
290 bool fPhysSG;
291 /** Explicit alignment. */
292 bool fUnused;
293 /** Busy count for tracking destination table references and active sends.
294 * Usually incremented while owning the switch table spinlock. The 30th bit
295 * is used to indicate wakeup. */
296 uint32_t volatile cBusy;
297 /** Mask of destinations that pfnXmit cope with disabled preemption for. */
298 uint32_t fNoPreemptDsts;
299 /** The GSO capabilities of the wire destination. (reported) */
300 uint32_t fWireGsoCapabilites;
301 /** The GSO capabilities of the host destination. (reported)
302 * This is as bit map where each bit represents the GSO type with the same
303 * number. */
304 uint32_t fHostGsoCapabilites;
305 /** Header buffer for when we're carving GSO frames. */
306 uint8_t abGsoHdrs[256];
307 /** The destination table spinlock, interrupt safe.
308 * Protects apTaskDstTabs and apIntDstTabs. */
309 RTSPINLOCK hDstTabSpinlock;
310 /** The number of entries in apIntDstTabs. */
311 uint32_t cIntDstTabs;
312 /** The task time destination tables.
313 * @remarks intnetR0NetworkEnsureTabSpace and others ASSUMES this immediately
314 * preceeds apIntDstTabs so that these two tables can be used as one
315 * contiguous one. */
316 PINTNETDSTTAB apTaskDstTabs[2];
317 /** The interrupt / disabled-preemption time destination tables.
318 * This is a variable sized array. */
319 PINTNETDSTTAB apIntDstTabs[1];
320} INTNETTRUNKIF;
321/** Pointer to a trunk interface. */
322typedef INTNETTRUNKIF *PINTNETTRUNKIF;
323
324/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
325#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
326
327
328/**
329 * Internal representation of a network.
330 */
331typedef struct INTNETNETWORK
332{
333 /** The Next network in the chain.
334 * This is protected by the INTNET::hMtxCreateOpenDestroy. */
335 struct INTNETNETWORK *pNext;
336
337 /** The spinlock protecting MacTab and INTNETTRUNKIF::aAddrCache.
338 * Interrupt safe. */
339 RTSPINLOCK hAddrSpinlock;
340 /** MAC address table.
341 * This doubles as interface collection. */
342 INTNETMACTAB MacTab;
343
344 /** Wait for an interface to stop being busy so it can be removed or have its
345 * destination table replaced. We have to wait upon this while owning the
346 * network mutex. Will only ever have one waiter because of the big mutex. */
347 RTSEMEVENT hEvtBusyIf;
348 /** Pointer to the instance data. */
349 struct INTNET *pIntNet;
350 /** The SUPR0 object id. */
351 void *pvObj;
352 /** Pointer to the temporary buffer that is used when snooping fragmented packets.
353 * This is allocated after this structure if we're sharing the MAC address with
354 * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */
355 uint8_t *pbTmp;
356 /** Network creation flags (INTNET_OPEN_FLAGS_*). */
357 uint32_t fFlags;
358 /** The number of active interfaces (excluding the trunk). */
359 uint32_t cActiveIFs;
360 /** The length of the network name. */
361 uint8_t cchName;
362 /** The network name. */
363 char szName[INTNET_MAX_NETWORK_NAME];
364 /** The trunk type. */
365 INTNETTRUNKTYPE enmTrunkType;
366 /** The trunk name. */
367 char szTrunk[INTNET_MAX_TRUNK_NAME];
368} INTNETNETWORK;
369/** Pointer to an internal network. */
370typedef INTNETNETWORK *PINTNETNETWORK;
371
372/** The size of the buffer INTNETNETWORK::pbTmp points at. */
373#define INTNETNETWORK_TMP_SIZE 2048
374
375
376/**
377 * Internal networking instance.
378 */
379typedef struct INTNET
380{
381 /** Magic number (INTNET_MAGIC). */
382 uint32_t volatile u32Magic;
383 /** Mutex protecting the creation, opening and destruction of both networks and
384 * interfaces. (This means all operations affecting the pNetworks list.) */
385 RTSEMMUTEX hMtxCreateOpenDestroy;
386 /** List of networks. Protected by INTNET::Spinlock. */
387 PINTNETNETWORK volatile pNetworks;
388 /** Handle table for the interfaces. */
389 RTHANDLETABLE hHtIfs;
390} INTNET;
391/** Pointer to an internal network ring-0 instance. */
392typedef struct INTNET *PINTNET;
393
394/** Magic number for the internal network instance data (Hayao Miyazaki). */
395#define INTNET_MAGIC UINT32_C(0x19410105)
396
397
398/*******************************************************************************
399* Global Variables *
400*******************************************************************************/
401/** Pointer to the internal network instance data. */
402static PINTNET volatile g_pIntNet = NULL;
403
404
405/*******************************************************************************
406* Internal Functions *
407*******************************************************************************/
408static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
409static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
410
411
412/**
413 * Worker for intnetR0SgWritePart that deals with the case where the
414 * request doesn't fit into the first segment.
415 *
416 * @returns true, unless the request or SG invalid.
417 * @param pSG The SG list to write to.
418 * @param off Where to start writing (offset into the SG).
419 * @param cb How much to write.
420 * @param pvBuf The buffer to containing the bits to write.
421 */
422static bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
423{
424 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
425 return false;
426
427 /*
428 * Skip ahead to the segment where off starts.
429 */
430 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
431 unsigned iSeg = 0;
432 while (off > pSG->aSegs[iSeg].cb)
433 {
434 off -= pSG->aSegs[iSeg++].cb;
435 AssertReturn(iSeg < cSegs, false);
436 }
437
438 /*
439 * Copy the data, hoping that it's all from one segment...
440 */
441 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
442 if (cbCanCopy >= cb)
443 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
444 else
445 {
446 /* copy the portion in the current segment. */
447 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
448 cb -= cbCanCopy;
449
450 /* copy the portions in the other segments. */
451 do
452 {
453 pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
454 iSeg++;
455 AssertReturn(iSeg < cSegs, false);
456
457 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
458 memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
459
460 cb -= cbCanCopy;
461 } while (cb > 0);
462 }
463
464 return true;
465}
466
467
468/**
469 * Writes to a part of an SG.
470 *
471 * @returns true on success, false on failure (out of bounds).
472 * @param pSG The SG list to write to.
473 * @param off Where to start writing (offset into the SG).
474 * @param cb How much to write.
475 * @param pvBuf The buffer to containing the bits to write.
476 */
477DECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
478{
479 Assert(off + cb > off);
480
481 /* The optimized case. */
482 if (RT_LIKELY( pSG->cSegsUsed == 1
483 || pSG->aSegs[0].cb >= off + cb))
484 {
485 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
486 memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
487 return true;
488 }
489 return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
490}
491
492
493/**
494 * Reads a byte from a SG list.
495 *
496 * @returns The byte on success. 0xff on failure.
497 * @param pSG The SG list to read.
498 * @param off The offset (into the SG) off the byte.
499 */
500DECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
501{
502 if (RT_LIKELY(pSG->aSegs[0].cb > off))
503 return ((uint8_t const *)pSG->aSegs[0].pv)[off];
504
505 off -= pSG->aSegs[0].cb;
506 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
507 for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
508 {
509 if (pSG->aSegs[iSeg].cb > off)
510 return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
511 off -= pSG->aSegs[iSeg].cb;
512 }
513 return false;
514}
515
516
517/**
518 * Worker for intnetR0SgReadPart that deals with the case where the
519 * requested data isn't in the first segment.
520 *
521 * @returns true, unless the SG is invalid.
522 * @param pSG The SG list to read.
523 * @param off Where to start reading (offset into the SG).
524 * @param cb How much to read.
525 * @param pvBuf The buffer to read into.
526 */
527static bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
528{
529 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
530 return false;
531
532 /*
533 * Skip ahead to the segment where off starts.
534 */
535 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
536 unsigned iSeg = 0;
537 while (off > pSG->aSegs[iSeg].cb)
538 {
539 off -= pSG->aSegs[iSeg++].cb;
540 AssertReturn(iSeg < cSegs, false);
541 }
542
543 /*
544 * Copy the data, hoping that it's all from one segment...
545 */
546 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
547 if (cbCanCopy >= cb)
548 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
549 else
550 {
551 /* copy the portion in the current segment. */
552 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
553 cb -= cbCanCopy;
554
555 /* copy the portions in the other segments. */
556 do
557 {
558 pvBuf = (uint8_t *)pvBuf + cbCanCopy;
559 iSeg++;
560 AssertReturn(iSeg < cSegs, false);
561
562 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
563 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
564
565 cb -= cbCanCopy;
566 } while (cb > 0);
567 }
568
569 return true;
570}
571
572
573/**
574 * Reads a part of an SG into a buffer.
575 *
576 * @returns true on success, false on failure (out of bounds).
577 * @param pSG The SG list to read.
578 * @param off Where to start reading (offset into the SG).
579 * @param cb How much to read.
580 * @param pvBuf The buffer to read into.
581 */
582DECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
583{
584 Assert(off + cb > off);
585
586 /* The optimized case. */
587 if (RT_LIKELY( pSG->cSegsUsed == 1
588 || pSG->aSegs[0].cb >= off + cb))
589 {
590 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
591 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
592 return true;
593 }
594 return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
595}
596
597
598/**
599 * Wait for a busy counter to reach zero.
600 *
601 * @param pNetwork The network.
602 * @param pcBusy The busy counter.
603 */
604static void intnetR0BusyWait(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
605{
606 if (ASMAtomicReadU32(pcBusy) == 0)
607 return;
608
609 /*
610 * We have to be a bit cautious here so we don't destroy the network or the
611 * semaphore before intnetR0BusyDec has signalled us.
612 */
613
614 /* Reset the semaphore and flip the wakeup bit. */
615 RTSemEventWait(pNetwork->hEvtBusyIf, 0); /* clear it */
616 uint32_t cCurBusy = ASMAtomicReadU32(pcBusy);
617 do
618 {
619 if (cCurBusy == 0)
620 return;
621 AssertMsg(!(cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
622 AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
623 } while (!ASMAtomicCmpXchgExU32(pcBusy, cCurBusy | INTNET_BUSY_WAKEUP_MASK, cCurBusy, &cCurBusy));
624
625 /* Wait for the count to reach zero. */
626 do
627 {
628 int rc2 = RTSemEventWait(pNetwork->hEvtBusyIf, 30000); NOREF(rc2);
629 //AssertMsg(RT_SUCCESS(rc2), ("rc=%Rrc *pcBusy=%#x (%#x)\n", rc2, ASMAtomicReadU32(pcBusy), cCurBusy ));
630 cCurBusy = ASMAtomicReadU32(pcBusy);
631 AssertMsg((cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
632 AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
633 } while ( cCurBusy != INTNET_BUSY_WAKEUP_MASK
634 || ASMAtomicCmpXchgU32(pcBusy, 0, INTNET_BUSY_WAKEUP_MASK));
635}
636
637
638/**
639 * Decrements the busy counter and maybe wakes up any threads waiting for it to
640 * reach zero.
641 *
642 * @param pNetwork The network.
643 * @param pcBusy The busy counter.
644 */
645DECLINLINE(void) intnetR0BusyDec(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
646{
647 uint32_t cNewBusy = ASMAtomicDecU32(pcBusy);
648 if (RT_UNLIKELY( cNewBusy == INTNET_BUSY_WAKEUP_MASK
649 && pNetwork))
650 RTSemEventSignal(pNetwork->hEvtBusyIf);
651 AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
652}
653
654
655/**
656 * Increments the busy count of the specified interface.
657 *
658 * The caller must own the MAC address table spinlock.
659 *
660 * @param pIf The interface.
661 */
662DECLINLINE(void) intnetR0BusyDecIf(PINTNETIF pIf)
663{
664 intnetR0BusyDec(pIf->pNetwork, &pIf->cBusy);
665}
666
667
668/**
669 * Increments the busy count of the specified interface.
670 *
671 * The caller must own the MAC address table spinlock or an explicity reference.
672 *
673 * @param pTrunk The trunk.
674 */
675DECLINLINE(void) intnetR0BusyDecTrunk(PINTNETTRUNKIF pTrunk)
676{
677 intnetR0BusyDec(pTrunk->pNetwork, &pTrunk->cBusy);
678}
679
680
681/**
682 * Increments the busy count of the specified interface.
683 *
684 * The caller must own the MAC address table spinlock or an explicity reference.
685 *
686 * @param pIf The interface.
687 */
688DECLINLINE(void) intnetR0BusyIncIf(PINTNETIF pIf)
689{
690 uint32_t cNewBusy = ASMAtomicIncU32(&pIf->cBusy);
691 AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
692 NOREF(cNewBusy);
693}
694
695
696/**
697 * Increments the busy count of the specified interface.
698 *
699 * The caller must own the MAC address table spinlock or an explicity reference.
700 *
701 * @param pTrunk The trunk.
702 */
703DECLINLINE(void) intnetR0BusyIncTrunk(PINTNETTRUNKIF pTrunk)
704{
705 uint32_t cNewBusy = ASMAtomicIncU32(&pTrunk->cBusy);
706 AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
707 NOREF(cNewBusy);
708}
709
710
711/**
712 * Retain an interface.
713 *
714 * @returns VBox status code, can assume success in most situations.
715 * @param pIf The interface instance.
716 * @param pSession The current session.
717 */
718DECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
719{
720 int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
721 AssertRCReturn(rc, rc);
722 return VINF_SUCCESS;
723}
724
725
726/**
727 * Release an interface previously retained by intnetR0IfRetain or
728 * by handle lookup/freeing.
729 *
730 * @returns true if destroyed, false if not.
731 * @param pIf The interface instance.
732 * @param pSession The current session.
733 */
734DECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
735{
736 int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
737 AssertRC(rc);
738 return rc == VINF_OBJECT_DESTROYED;
739}
740
741
742/**
743 * RTHandleCreateEx callback that retains an object in the
744 * handle table before returning it.
745 *
746 * (Avoids racing the freeing of the handle.)
747 *
748 * @returns VBox status code.
749 * @param hHandleTable The handle table (ignored).
750 * @param pvObj The object (INTNETIF).
751 * @param pvCtx The context (SUPDRVSESSION).
752 * @param pvUser The user context (ignored).
753 */
754static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
755{
756 NOREF(pvUser);
757 NOREF(hHandleTable);
758 PINTNETIF pIf = (PINTNETIF)pvObj;
759 if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
760 return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
761 return VINF_SUCCESS;
762}
763
764
765
766/**
767 * Checks if the interface has a usable MAC address or not.
768 *
769 * @returns true if MacAddr is usable, false if not.
770 * @param pIf The interface.
771 */
772DECL_FORCE_INLINE(bool) intnetR0IfHasMacAddr(PINTNETIF pIf)
773{
774 return pIf->fMacSet || !(pIf->MacAddr.au8[0] & 1);
775}
776
777
778/**
779 * Locates the MAC address table entry for the given interface.
780 *
781 * The caller holds the MAC address table spinlock, obviously.
782 *
783 * @returns Pointer to the entry on if found, NULL if not.
784 * @param pNetwork The network.
785 * @param pIf The interface.
786 */
787DECLINLINE(PINTNETMACTABENTRY) intnetR0NetworkFindMacAddrEntry(PINTNETNETWORK pNetwork, PINTNETIF pIf)
788{
789 uint32_t iIf = pNetwork->MacTab.cEntries;
790 while (iIf-- > 0)
791 {
792 if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
793 return &pNetwork->MacTab.paEntries[iIf];
794 }
795 return NULL;
796}
797
798
799/**
800 * Checks if the IPv4 address is a broadcast address.
801 * @returns true/false.
802 * @param Addr The address, network endian.
803 */
804DECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
805{
806 /* Just check for 255.255.255.255 atm. */
807 return Addr.u == UINT32_MAX;
808}
809
810
811/**
812 * Checks if the IPv4 address is a good interface address.
813 * @returns true/false.
814 * @param Addr The address, network endian.
815 */
816DECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
817{
818 /* Usual suspects. */
819 if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
820 || Addr.au8[0] == 0) /* Current network, can be used as source address. */
821 return false;
822
823 /* Unusual suspects. */
824 if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
825 || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
826 ))
827 return false;
828 return true;
829}
830
831
832/**
833 * Gets the address size of a network layer type.
834 *
835 * @returns size in bytes.
836 * @param enmType The type.
837 */
838DECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
839{
840 switch (enmType)
841 {
842 case kIntNetAddrType_IPv4: return 4;
843 case kIntNetAddrType_IPv6: return 16;
844 case kIntNetAddrType_IPX: return 4 + 6;
845 default: AssertFailedReturn(0);
846 }
847}
848
849
850/**
851 * Compares two address to see if they are equal, assuming naturally align structures.
852 *
853 * @returns true if equal, false if not.
854 * @param pAddr1 The first address.
855 * @param pAddr2 The second address.
856 * @param cbAddr The address size.
857 */
858DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
859{
860 switch (cbAddr)
861 {
862 case 4: /* IPv4 */
863 return pAddr1->au32[0] == pAddr2->au32[0];
864 case 16: /* IPv6 */
865 return pAddr1->au64[0] == pAddr2->au64[0]
866 && pAddr1->au64[1] == pAddr2->au64[1];
867 case 10: /* IPX */
868 return pAddr1->au64[0] == pAddr2->au64[0]
869 && pAddr1->au16[4] == pAddr2->au16[4];
870 default:
871 AssertFailedReturn(false);
872 }
873}
874
875
876/**
877 * Worker for intnetR0IfAddrCacheLookup that performs the lookup
878 * in the remaining cache entries after the caller has check the
879 * most likely ones.
880 *
881 * @returns -1 if not found, the index of the cache entry if found.
882 * @param pCache The cache.
883 * @param pAddr The address.
884 * @param cbAddr The address size (optimization).
885 */
886static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
887{
888 unsigned i = pCache->cEntries - 2;
889 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
890 while (i >= 1)
891 {
892 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
893 return i;
894 pbEntry -= pCache->cbEntry;
895 i--;
896 }
897
898 return -1;
899}
900
901/**
902 * Lookup an address in a cache without any expectations.
903 *
904 * @returns -1 if not found, the index of the cache entry if found.
905 * @param pCache The cache.
906 * @param pAddr The address.
907 * @param cbAddr The address size (optimization).
908 */
909DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
910{
911 Assert(pCache->cbAddress == cbAddr);
912
913 /*
914 * The optimized case is when there is one cache entry and
915 * it doesn't match.
916 */
917 unsigned i = pCache->cEntries;
918 if ( i > 0
919 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
920 return 0;
921 if (i <= 1)
922 return -1;
923
924 /*
925 * Check the last entry.
926 */
927 i--;
928 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
929 return i;
930 if (i <= 1)
931 return -1;
932
933 return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
934}
935
936
937/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
938DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
939{
940 /** @todo implement this. */
941 return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
942}
943
944
945/**
946 * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
947 * the lookup in the remaining cache entries after the caller
948 * has check the most likely ones.
949 *
950 * The routine is expecting not to find the address.
951 *
952 * @returns -1 if not found, the index of the cache entry if found.
953 * @param pCache The cache.
954 * @param pAddr The address.
955 * @param cbAddr The address size (optimization).
956 */
957static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
958{
959 /*
960 * Perform a full table lookup.
961 */
962 unsigned i = pCache->cEntries - 2;
963 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
964 while (i >= 1)
965 {
966 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
967 return i;
968 pbEntry -= pCache->cbEntry;
969 i--;
970 }
971
972 return -1;
973}
974
975
976/**
977 * Lookup an address in a cache expecting not to find it.
978 *
979 * @returns -1 if not found, the index of the cache entry if found.
980 * @param pCache The cache.
981 * @param pAddr The address.
982 * @param cbAddr The address size (optimization).
983 */
984DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
985{
986 Assert(pCache->cbAddress == cbAddr);
987
988 /*
989 * The optimized case is when there is one cache entry and
990 * it doesn't match.
991 */
992 unsigned i = pCache->cEntries;
993 if (RT_UNLIKELY( i > 0
994 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
995 return 0;
996 if (RT_LIKELY(i <= 1))
997 return -1;
998
999 /*
1000 * Then check the last entry and return if there are just two cache entries.
1001 */
1002 i--;
1003 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
1004 return i;
1005 if (i <= 1)
1006 return -1;
1007
1008 return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
1009}
1010
1011
1012/**
1013 * Deletes a specific cache entry.
1014 *
1015 * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
1016 *
1017 * @param pIf The interface (for logging).
1018 * @param pCache The cache.
1019 * @param iEntry The entry to delete.
1020 * @param pszMsg Log message.
1021 */
1022static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
1023{
1024 AssertReturnVoid(iEntry < pCache->cEntries);
1025 AssertReturnVoid(iEntry >= 0);
1026#ifdef LOG_ENABLED
1027 INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
1028 PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
1029 switch (enmAddrType)
1030 {
1031 case kIntNetAddrType_IPv4:
1032 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
1033 pIf->hIf, &pIf->MacAddr, iEntry, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
1034 break;
1035 default:
1036 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
1037 pIf->hIf, &pIf->MacAddr, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
1038 break;
1039 }
1040#endif
1041
1042 pCache->cEntries--;
1043 if (iEntry < pCache->cEntries)
1044 memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
1045 pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
1046 (pCache->cEntries - iEntry) * pCache->cbEntry);
1047}
1048
1049
1050/**
1051 * Deletes an address from the cache, assuming it isn't actually in the cache.
1052 *
1053 * May or may not own the spinlock when calling this.
1054 *
1055 * @param pIf The interface (for logging).
1056 * @param pCache The cache.
1057 * @param pAddr The address.
1058 * @param cbAddr The address size (optimization).
1059 */
1060DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
1061{
1062 int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
1063 if (RT_UNLIKELY(i >= 0))
1064 intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
1065}
1066
1067
1068/**
1069 * Deletes the address from all the interface caches.
1070 *
1071 * This is used to remove stale entries that has been reassigned to
1072 * other machines on the network.
1073 *
1074 * @param pNetwork The network.
1075 * @param pAddr The address.
1076 * @param enmType The address type.
1077 * @param cbAddr The address size (optimization).
1078 * @param pszMsg Log message.
1079 */
1080DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
1081 uint8_t const cbAddr, const char *pszMsg)
1082{
1083 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1084 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1085
1086 uint32_t iIf = pNetwork->MacTab.cEntries;
1087 while (iIf--)
1088 {
1089 PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
1090 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
1091 if (RT_UNLIKELY(i >= 0))
1092 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
1093 }
1094
1095 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1096}
1097
1098
1099/**
1100 * Deletes the address from all the interface caches except the specified one.
1101 *
1102 * This is used to remove stale entries that has been reassigned to
1103 * other machines on the network.
1104 *
1105 * @param pNetwork The network.
1106 * @param pAddr The address.
1107 * @param enmType The address type.
1108 * @param cbAddr The address size (optimization).
1109 * @param pszMsg Log message.
1110 */
1111DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
1112 INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
1113{
1114 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1115 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1116
1117 uint32_t iIf = pNetwork->MacTab.cEntries;
1118 while (iIf--)
1119 {
1120 PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
1121 if (pIf != pIfSender)
1122 {
1123 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
1124 if (RT_UNLIKELY(i >= 0))
1125 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
1126 }
1127 }
1128
1129 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1130}
1131
1132
1133/**
1134 * Lookup an address on the network, returning the (first) interface having it
1135 * in its address cache.
1136 *
1137 * @returns Pointer to the interface on success, NULL if not found. The caller
1138 * must release the interface by calling intnetR0BusyDecIf.
1139 * @param pNetwork The network.
1140 * @param pAddr The address to lookup.
1141 * @param enmType The address type.
1142 * @param cbAddr The size of the address.
1143 */
1144DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
1145{
1146 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1147 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1148
1149 uint32_t iIf = pNetwork->MacTab.cEntries;
1150 while (iIf--)
1151 {
1152 PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
1153 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
1154 if (i >= 0)
1155 {
1156 intnetR0BusyIncIf(pIf);
1157 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1158 return pIf;
1159 }
1160 }
1161
1162 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1163 return NULL;
1164}
1165
1166
1167/**
1168 * Adds an address to the cache, the caller is responsible for making sure it's
1169 * not already in the cache.
1170 *
1171 * The caller must not
1172 *
1173 * @param pIf The interface (for logging).
1174 * @param pCache The address cache.
1175 * @param pAddr The address.
1176 * @param pszMsg log message.
1177 */
1178static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
1179{
1180 PINTNETNETWORK pNetwork = pIf->pNetwork;
1181 AssertReturnVoid(pNetwork);
1182 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1183 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1184
1185 if (RT_UNLIKELY(!pCache->cEntriesAlloc))
1186 {
1187 /* This shouldn't happen*/
1188 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1189 return;
1190 }
1191
1192 /* When the table is full, drop the older entry (FIFO). Do proper ageing? */
1193 if (pCache->cEntries >= pCache->cEntriesAlloc)
1194 {
1195 Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
1196 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
1197 memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
1198 pCache->cEntries--;
1199 Assert(pCache->cEntries < pCache->cEntriesAlloc);
1200 }
1201
1202 /*
1203 * Add the new entry to the end of the array.
1204 */
1205 uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
1206 memcpy(pbEntry, pAddr, pCache->cbAddress);
1207 memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
1208#ifdef LOG_ENABLED
1209 INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
1210 switch (enmAddrType)
1211 {
1212 case kIntNetAddrType_IPv4:
1213 Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %d.%d.%d.%d %s\n",
1214 pIf->hIf, &pIf->MacAddr, pCache->cEntries, pAddr->au8[0], pAddr->au8[1], pAddr->au8[2], pAddr->au8[3], pszMsg));
1215 break;
1216 default:
1217 Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
1218 pIf->hIf, &pIf->MacAddr, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
1219 break;
1220 }
1221#endif
1222 pCache->cEntries++;
1223 Assert(pCache->cEntries <= pCache->cEntriesAlloc);
1224
1225 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1226}
1227
1228
1229/**
1230 * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
1231 *
1232 * @param pIf The interface (for logging).
1233 * @param pCache The address cache.
1234 * @param pAddr The address.
1235 * @param cbAddr The size of the address (optimization).
1236 * @param pszMsg Log message.
1237 */
1238static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
1239{
1240 /*
1241 * Check all but the first and last entries, the caller
1242 * has already checked those.
1243 */
1244 int i = pCache->cEntries - 2;
1245 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
1246 while (i >= 1)
1247 {
1248 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
1249 return;
1250 pbEntry += pCache->cbEntry;
1251 i--;
1252 }
1253
1254 /*
1255 * Not found, add it.
1256 */
1257 intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr, pszMsg);
1258}
1259
1260
1261/**
1262 * Adds an address to the cache if it's not already there.
1263 *
1264 * Must not own any spinlocks when calling this function.
1265 *
1266 * @param pIf The interface (for logging).
1267 * @param pCache The address cache.
1268 * @param pAddr The address.
1269 * @param cbAddr The size of the address (optimization).
1270 * @param pszMsg Log message.
1271 */
1272DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr,
1273 uint8_t const cbAddr, const char *pszMsg)
1274{
1275 Assert(pCache->cbAddress == cbAddr);
1276
1277 /*
1278 * The optimized case is when the address the first or last cache entry.
1279 */
1280 unsigned i = pCache->cEntries;
1281 if (RT_LIKELY( i > 0
1282 && ( intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)
1283 || (i > 1
1284 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))) ))
1285 return;
1286 intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr, pszMsg);
1287}
1288
1289
1290/**
1291 * Destroys the specified address cache.
1292 * @param pCache The address cache.
1293 */
1294static void intnetR0IfAddrCacheDestroy(PINTNETADDRCACHE pCache)
1295{
1296 void *pvFree = pCache->pbEntries;
1297 pCache->pbEntries = NULL;
1298 pCache->cEntries = 0;
1299 pCache->cEntriesAlloc = 0;
1300 RTMemFree(pvFree);
1301}
1302
1303
1304/**
1305 * Initialize the address cache for the specified address type.
1306 *
1307 * The cache storage is preallocated and fixed size so that we can handle
1308 * inserts from problematic contexts.
1309 *
1310 * @returns VINF_SUCCESS or VERR_NO_MEMORY.
1311 * @param pCache The cache to initialize.
1312 * @param enmAddrType The address type.
1313 * @param fEnabled Whether the address cache is enabled or not.
1314 */
1315static int intnetR0IfAddrCacheInit(PINTNETADDRCACHE pCache, INTNETADDRTYPE enmAddrType, bool fEnabled)
1316{
1317 pCache->cEntries = 0;
1318 pCache->cbAddress = intnetR0AddrSize(enmAddrType);
1319 pCache->cbEntry = RT_ALIGN(pCache->cbAddress, 4);
1320 if (fEnabled)
1321 {
1322 pCache->cEntriesAlloc = 32;
1323 pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cEntriesAlloc * pCache->cbEntry);
1324 if (!pCache->pbEntries)
1325 return VERR_NO_MEMORY;
1326 }
1327 else
1328 {
1329 pCache->cEntriesAlloc = 0;
1330 pCache->pbEntries = NULL;
1331 }
1332 return VINF_SUCCESS;
1333}
1334
1335
1336/**
1337 * Is it a multicast or broadcast MAC address?
1338 *
1339 * @returns true if multicast, false if not.
1340 * @param pMacAddr The address to inspect.
1341 */
1342DECL_FORCE_INLINE(bool) intnetR0IsMacAddrMulticast(PCRTMAC pMacAddr)
1343{
1344 return !!(pMacAddr->au8[0] & 0x01);
1345}
1346
1347
1348/**
1349 * Is it a dummy MAC address?
1350 *
1351 * We use dummy MAC addresses for interfaces which we don't know the MAC
1352 * address of because they haven't sent anything (learning) or explicitly set
1353 * it.
1354 *
1355 * @returns true if dummy, false if not.
1356 * @param pMacAddr The address to inspect.
1357 */
1358DECL_FORCE_INLINE(bool) intnetR0IsMacAddrDummy(PCRTMAC pMacAddr)
1359{
1360 /* The dummy address are broadcast addresses, don't bother check it all. */
1361 return pMacAddr->au16[0] == 0xffff;
1362}
1363
1364
1365/**
1366 * Compares two MAC addresses.
1367 *
1368 * @returns true if equal, false if not.
1369 * @param pDstAddr1 Address 1.
1370 * @param pDstAddr2 Address 2.
1371 */
1372DECL_FORCE_INLINE(bool) intnetR0AreMacAddrsEqual(PCRTMAC pDstAddr1, PCRTMAC pDstAddr2)
1373{
1374 return pDstAddr1->au16[2] == pDstAddr2->au16[2]
1375 && pDstAddr1->au16[1] == pDstAddr2->au16[1]
1376 && pDstAddr1->au16[0] == pDstAddr2->au16[0];
1377}
1378
1379
1380/**
1381 * Switch a unicast frame based on the network layer address (OSI level 3) and
1382 * return a destination table.
1383 *
1384 * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
1385 * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
1386 * @param pNetwork The network to switch on.
1387 * @param pDstMacAddr The destination MAC address.
1388 * @param enmL3AddrType The level-3 destination address type.
1389 * @param pL3Addr The level-3 destination address.
1390 * @param cbL3Addr The size of the level-3 destination address.
1391 * @param fSrc The frame source (INTNETTRUNKDIR_WIRE).
1392 * @param pDstTab The destination output table.
1393 */
1394static INTNETSWDECISION intnetR0NetworkSwitchLevel3(PINTNETNETWORK pNetwork, PCRTMAC pDstMacAddr,
1395 INTNETADDRTYPE enmL3AddrType, PCRTNETADDRU pL3Addr, uint8_t cbL3Addr,
1396 uint32_t fSrc, PINTNETDSTTAB pDstTab)
1397{
1398 Assert(fSrc == INTNETTRUNKDIR_WIRE);
1399
1400 /*
1401 * Grab the spinlock first and do the switching.
1402 */
1403 PINTNETMACTAB pTab = &pNetwork->MacTab;
1404 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1405 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1406
1407 pDstTab->fTrunkDst = 0;
1408 pDstTab->pTrunk = 0;
1409 pDstTab->cIfs = 0;
1410
1411 /* Find exactly matching or promiscuous interfaces. */
1412 uint32_t cExactHits = 0;
1413 uint32_t iIfMac = pTab->cEntries;
1414 while (iIfMac-- > 0)
1415 {
1416 if (pTab->paEntries[iIfMac].fActive)
1417 {
1418 PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
1419 bool fExact = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) >= 0;
1420 if (fExact || pTab->paEntries[iIfMac].fPromiscuous)
1421 {
1422 cExactHits += fExact;
1423
1424 uint32_t iIfDst = pDstTab->cIfs++;
1425 pDstTab->aIfs[iIfDst].pIf = pIf;
1426 pDstTab->aIfs[iIfDst].fReplaceDstMac = fExact;
1427 intnetR0BusyIncIf(pIf);
1428 }
1429 }
1430 }
1431
1432 /* Does it match the host, or is the host promiscuous? */
1433 if (pTab->fHostActive)
1434 {
1435 bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstMacAddr);
1436 if ( fExact
1437 || intnetR0IsMacAddrDummy(&pTab->HostMac)
1438 || pTab->fHostPromiscuous)
1439 {
1440 cExactHits += fExact;
1441 pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
1442 }
1443 }
1444
1445 /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
1446 if (pTab->fWireActive && (!cExactHits || pTab->fWirePromiscuous))
1447 pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
1448 pDstTab->fTrunkDst &= ~fSrc;
1449 if (pDstTab->fTrunkDst)
1450 {
1451 PINTNETTRUNKIF pTrunk = pTab->pTrunk;
1452 pDstTab->pTrunk = pTrunk;
1453 intnetR0BusyIncTrunk(pTrunk);
1454 }
1455
1456 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1457 return pDstTab->cIfs
1458 ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
1459 : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
1460}
1461
1462
1463/**
1464 * Switch a unicast MAC address and return a destination table.
1465 *
1466 * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
1467 * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
1468 * @param pNetwork The network to switch on.
1469 * @param fSrc The frame source.
1470 * @param pIfSender The sender interface, NULL if trunk. Used to
1471 * prevent sending an echo to the sender.
1472 * @param pDstAddr The destination address of the frame.
1473 * @param pDstTab The destination output table.
1474 */
1475static INTNETSWDECISION intnetR0NetworkSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
1476 PCRTMAC pDstAddr, PINTNETDSTTAB pDstTab)
1477{
1478 AssertPtr(pDstTab);
1479 Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
1480
1481 /*
1482 * Grab the spinlock first and do the switching.
1483 */
1484 PINTNETMACTAB pTab = &pNetwork->MacTab;
1485 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1486 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1487
1488 pDstTab->fTrunkDst = 0;
1489 pDstTab->pTrunk = 0;
1490 pDstTab->cIfs = 0;
1491
1492 /* Find exactly matching or promiscuous interfaces. */
1493 uint32_t cExactHits = 0;
1494 uint32_t iIfMac = pTab->cEntries;
1495 while (iIfMac-- > 0)
1496 {
1497 if (pTab->paEntries[iIfMac].fActive)
1498 {
1499 bool fExact = intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr);
1500 if ( fExact
1501 || intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr)
1502 || pTab->paEntries[iIfMac].fPromiscuous)
1503 {
1504 cExactHits += fExact;
1505
1506 PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
1507 if (RT_LIKELY(pIf != pIfSender)) /* paranoia */
1508 {
1509 uint32_t iIfDst = pDstTab->cIfs++;
1510 pDstTab->aIfs[iIfDst].pIf = pIf;
1511 pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
1512 intnetR0BusyIncIf(pIf);
1513 }
1514 }
1515 }
1516 }
1517
1518 /* Does it match the host, or is the host promiscuous? */
1519 if ( fSrc != INTNETTRUNKDIR_HOST
1520 && pTab->fHostActive)
1521 {
1522 bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstAddr);
1523 if ( fExact
1524 || intnetR0IsMacAddrDummy(&pTab->HostMac)
1525 || pTab->fHostPromiscuous)
1526 {
1527 cExactHits += fExact;
1528 pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
1529 }
1530 }
1531
1532 /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
1533 if ( fSrc != INTNETTRUNKDIR_WIRE
1534 && pTab->fWireActive
1535 && (!cExactHits || pTab->fWirePromiscuous)
1536 )
1537 pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
1538
1539 /* Grab the trunk if we're sending to it. */
1540 if (pDstTab->fTrunkDst)
1541 {
1542 PINTNETTRUNKIF pTrunk = pTab->pTrunk;
1543 pDstTab->pTrunk = pTrunk;
1544 intnetR0BusyIncTrunk(pTrunk);
1545 }
1546
1547 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1548 return pDstTab->cIfs
1549 ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
1550 : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
1551}
1552
1553
1554/**
1555 * Create a destination table for a broadcast frame.
1556 *
1557 * @returns INTNETSWDECISION_BROADCAST.
1558 * @param pNetwork The network to switch on.
1559 * @param fSrc The frame source.
1560 * @param pIfSender The sender interface, NULL if trunk. Used to
1561 * prevent sending an echo to the sender.
1562 * @param pDstTab The destination output table.
1563 */
1564static INTNETSWDECISION intnetR0NetworkSwitchBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
1565 PINTNETDSTTAB pDstTab)
1566{
1567 AssertPtr(pDstTab);
1568
1569 /*
1570 * Grab the spinlock first and record all active interfaces.
1571 */
1572 PINTNETMACTAB pTab = &pNetwork->MacTab;
1573 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1574 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1575
1576 pDstTab->fTrunkDst = 0;
1577 pDstTab->pTrunk = 0;
1578 pDstTab->cIfs = 0;
1579
1580 /* Regular interfaces. */
1581 uint32_t iIfMac = pTab->cEntries;
1582 while (iIfMac-- > 0)
1583 {
1584 if (pTab->paEntries[iIfMac].fActive)
1585 {
1586 PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
1587 if (pIf != pIfSender)
1588 {
1589 uint32_t iIfDst = pDstTab->cIfs++;
1590 pDstTab->aIfs[iIfDst].pIf = pIf;
1591 pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
1592 intnetR0BusyIncIf(pIf);
1593 }
1594 }
1595 }
1596
1597 /* The trunk interface. */
1598 if (pTab->fHostActive)
1599 pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
1600 if (pTab->fWireActive)
1601 pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
1602 pDstTab->fTrunkDst &= ~fSrc;
1603 if (pDstTab->fTrunkDst)
1604 {
1605 PINTNETTRUNKIF pTrunk = pTab->pTrunk;
1606 pDstTab->pTrunk = pTrunk;
1607 intnetR0BusyIncTrunk(pTrunk);
1608 }
1609
1610 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1611 return INTNETSWDECISION_BROADCAST;
1612}
1613
1614
1615/**
1616 * Create a destination table with the trunk and any promiscuous interfaces.
1617 *
1618 * This is only used in a fallback case of the level-3 switching, so we can
1619 * assume the wire as source and skip the sender interface filtering.
1620 *
1621 * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
1622 * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
1623 * @param pNetwork The network to switch on.
1624 * @param fSrc The frame source.
1625 * @param pDstTab The destination output table.
1626 */
1627static INTNETSWDECISION intnetR0NetworkSwitchTrunkAndPromisc(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
1628{
1629 Assert(fSrc == INTNETTRUNKDIR_WIRE);
1630
1631 /*
1632 * Grab the spinlock first and do the switching.
1633 */
1634 PINTNETMACTAB pTab = &pNetwork->MacTab;
1635 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1636 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1637
1638 pDstTab->fTrunkDst = 0;
1639 pDstTab->pTrunk = 0;
1640 pDstTab->cIfs = 0;
1641
1642 /* Find promiscuous interfaces. */
1643 uint32_t iIfMac = pTab->cEntries;
1644 while (iIfMac-- > 0)
1645 {
1646 if ( pTab->paEntries[iIfMac].fActive
1647 && pTab->paEntries[iIfMac].fPromiscuous)
1648 {
1649 PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
1650 uint32_t iIfDst = pDstTab->cIfs++;
1651 pDstTab->aIfs[iIfDst].pIf = pIf;
1652 pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
1653 intnetR0BusyIncIf(pIf);
1654 }
1655 }
1656
1657 /* The trunk interface. */
1658 if (pTab->fHostActive)
1659 pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
1660 if (pTab->fWireActive)
1661 pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
1662 pDstTab->fTrunkDst &= ~fSrc;
1663 if (pDstTab->fTrunkDst)
1664 {
1665 PINTNETTRUNKIF pTrunk = pTab->pTrunk;
1666 pDstTab->pTrunk = pTrunk;
1667 intnetR0BusyIncTrunk(pTrunk);
1668 }
1669
1670 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1671 return !pDstTab->cIfs
1672 ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK)
1673 : (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST);
1674}
1675
1676
1677/**
1678 * Create a destination table for a trunk frame.
1679 *
1680 * @returns INTNETSWDECISION_BROADCAST.
1681 * @param pNetwork The network to switch on.
1682 * @param fSrc The frame source.
1683 * @param pDstTab The destination output table.
1684 */
1685static INTNETSWDECISION intnetR0NetworkSwitchTrunk(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
1686{
1687 AssertPtr(pDstTab);
1688
1689 /*
1690 * Grab the spinlock first and record all active interfaces.
1691 */
1692 PINTNETMACTAB pTab= &pNetwork->MacTab;
1693 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1694 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1695
1696 pDstTab->fTrunkDst = 0;
1697 pDstTab->pTrunk = 0;
1698 pDstTab->cIfs = 0;
1699
1700 /* The trunk interface. */
1701 if (pTab->fHostActive)
1702 pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
1703 if (pTab->fWireActive)
1704 pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
1705 pDstTab->fTrunkDst &= ~fSrc;
1706 if (pDstTab->fTrunkDst)
1707 {
1708 PINTNETTRUNKIF pTrunk = pTab->pTrunk;
1709 pDstTab->pTrunk = pTrunk;
1710 intnetR0BusyIncTrunk(pTrunk);
1711 }
1712
1713 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1714 return pDstTab->fTrunkDst ? INTNETSWDECISION_TRUNK : INTNETSWDECISION_DROP;
1715}
1716
1717
1718/**
1719 * Wrapper around RTMemAlloc for allocating a destination table.
1720 *
1721 * @returns VINF_SUCCESS or VERR_NO_MEMORY.
1722 * @param cEntries The size given as an entry count.
1723 * @param ppDstTab Where to store the pointer (always).
1724 */
1725DECLINLINE(int) intnetR0AllocDstTab(uint32_t cEntries, PINTNETDSTTAB *ppDstTab)
1726{
1727 PINTNETDSTTAB pDstTab;
1728 *ppDstTab = pDstTab = (PINTNETDSTTAB)RTMemAlloc(RT_OFFSETOF(INTNETDSTTAB, aIfs[cEntries]));
1729 if (RT_UNLIKELY(!pDstTab))
1730 return VERR_NO_MEMORY;
1731 return VINF_SUCCESS;
1732}
1733
1734
1735/**
1736 * Ensures that there is space for another interface in the MAC address lookup
1737 * table as well as all the destination tables.
1738 *
1739 * The caller must own the create/open/destroy mutex.
1740 *
1741 * @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_OUT_OF_RANGE.
1742 * @param pNetwork The network to operate on.
1743 */
1744static int intnetR0NetworkEnsureTabSpace(PINTNETNETWORK pNetwork)
1745{
1746 /*
1747 * The cEntries and cEntriesAllocated members are only updated while
1748 * owning the big mutex, so we only need the spinlock when doing the
1749 * actual table replacing.
1750 */
1751 PINTNETMACTAB pTab = &pNetwork->MacTab;
1752 int rc = VINF_SUCCESS;
1753 AssertReturn(pTab->cEntries <= pTab->cEntriesAllocated, VERR_INTERNAL_ERROR_2);
1754 if (pTab->cEntries + 1 > pTab->cEntriesAllocated)
1755 {
1756 uint32_t const cAllocated = pTab->cEntriesAllocated + INTNET_GROW_DSTTAB_SIZE;
1757 if (cAllocated <= INTNET_MAX_IFS)
1758 {
1759 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1760
1761 /*
1762 * Resize the destination tables first, this can be kind of tedious.
1763 */
1764 for (uint32_t i = 0; i < pTab->cEntries; i++)
1765 {
1766 PINTNETIF pIf = pTab->paEntries[i].pIf; AssertPtr(pIf);
1767 PINTNETDSTTAB pNew;
1768 rc = intnetR0AllocDstTab(cAllocated, &pNew);
1769 if (RT_FAILURE(rc))
1770 break;
1771
1772 for (;;)
1773 {
1774 PINTNETDSTTAB pOld = pIf->pDstTab;
1775 if ( pOld
1776 && ASMAtomicCmpXchgPtr((void * volatile *)&pIf->pDstTab, pNew, pOld))
1777 {
1778 RTMemFree(pOld);
1779 break;
1780 }
1781 intnetR0BusyWait(pNetwork, &pIf->cBusy);
1782 }
1783 }
1784
1785 /*
1786 * The trunk.
1787 */
1788 if ( RT_SUCCESS(rc)
1789 && pNetwork->MacTab.pTrunk)
1790 {
1791 AssertCompileAdjacentMembers(INTNETTRUNKIF, apTaskDstTabs, apIntDstTabs);
1792 PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
1793 PINTNETDSTTAB * const ppEndDstTab = &pTrunk->apIntDstTabs[pTrunk->cIntDstTabs];
1794 for (PINTNETDSTTAB *ppDstTab = &pTrunk->apTaskDstTabs[0];
1795 ppDstTab != ppEndDstTab && RT_SUCCESS(rc);
1796 ppDstTab++)
1797 {
1798 PINTNETDSTTAB pNew;
1799 rc = intnetR0AllocDstTab(cAllocated, &pNew);
1800 if (RT_FAILURE(rc))
1801 break;
1802
1803 for (;;)
1804 {
1805 RTSpinlockAcquireNoInts(pTrunk->hDstTabSpinlock, &Tmp);
1806 void *pvOld = *ppDstTab;
1807 if (pvOld)
1808 *ppDstTab = pNew;
1809 RTSpinlockReleaseNoInts(pTrunk->hDstTabSpinlock, &Tmp);
1810 if (pvOld)
1811 {
1812 RTMemFree(pvOld);
1813 break;
1814 }
1815 intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
1816 }
1817 }
1818 }
1819
1820 /*
1821 * The MAC Address table itself.
1822 */
1823 if (RT_SUCCESS(rc))
1824 {
1825 PINTNETMACTABENTRY paNew = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * cAllocated);
1826 if (paNew)
1827 {
1828 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1829
1830 PINTNETMACTABENTRY paOld = pTab->paEntries;
1831 uint32_t i = pTab->cEntries;
1832 while (i-- > 0)
1833 {
1834 paNew[i] = paOld[i];
1835
1836 paOld[i].fActive = false;
1837 paOld[i].pIf = NULL;
1838 }
1839
1840 pTab->paEntries = paNew;
1841 pTab->cEntriesAllocated = cAllocated;
1842
1843 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1844
1845 RTMemFree(paOld);
1846 }
1847 else
1848 rc = VERR_NO_MEMORY;
1849 }
1850 }
1851 else
1852 rc = VERR_OUT_OF_RANGE;
1853 }
1854 return rc;
1855}
1856
1857
1858
1859
1860#ifdef INTNET_WITH_DHCP_SNOOPING
1861
1862/**
1863 * Snoops IP assignments and releases from the DHCPv4 traffic.
1864 *
1865 * The caller is responsible for making sure this traffic between the
1866 * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet
1867 * need not be validated beyond the ports.
1868 *
1869 * @param pNetwork The network this frame was seen on.
1870 * @param pIpHdr Pointer to a valid IP header. This is for pseudo
1871 * header validation, so only the minimum header size
1872 * needs to be available and valid here.
1873 * @param pUdpHdr Pointer to the UDP header in the frame.
1874 * @param cbUdpPkt What's left of the frame when starting at the UDP header.
1875 * @param fGso Set if this is a GSO frame, clear if regular.
1876 */
1877static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
1878{
1879 /*
1880 * Check if the DHCP message is valid and get the type.
1881 */
1882 if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt, true /*fCheckSum*/))
1883 {
1884 Log6(("Bad UDP packet\n"));
1885 return;
1886 }
1887 PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
1888 uint8_t MsgType;
1889 if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
1890 {
1891 Log6(("Bad DHCP packet\n"));
1892 return;
1893 }
1894
1895#ifdef LOG_ENABLED
1896 /*
1897 * Log it.
1898 */
1899 const char *pszType = "unknown";
1900 switch (MsgType)
1901 {
1902 case RTNET_DHCP_MT_DISCOVER: pszType = "discover"; break;
1903 case RTNET_DHCP_MT_OFFER: pszType = "offer"; break;
1904 case RTNET_DHCP_MT_REQUEST: pszType = "request"; break;
1905 case RTNET_DHCP_MT_DECLINE: pszType = "decline"; break;
1906 case RTNET_DHCP_MT_ACK: pszType = "ack"; break;
1907 case RTNET_DHCP_MT_NAC: pszType = "nac"; break;
1908 case RTNET_DHCP_MT_RELEASE: pszType = "release"; break;
1909 case RTNET_DHCP_MT_INFORM: pszType = "inform"; break;
1910 }
1911 Log6(("DHCP msg: %d (%s) client %.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d\n", MsgType, pszType, &pDhcp->bp_chaddr,
1912 pDhcp->bp_ciaddr.au8[0], pDhcp->bp_ciaddr.au8[1], pDhcp->bp_ciaddr.au8[2], pDhcp->bp_ciaddr.au8[3],
1913 pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
1914#endif /* LOG_EANBLED */
1915
1916 /*
1917 * Act upon the message.
1918 */
1919 switch (MsgType)
1920 {
1921#if 0
1922 case RTNET_DHCP_MT_REQUEST:
1923 /** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we
1924 * know, and add the IP to the cache. */
1925 break;
1926#endif
1927
1928
1929 /*
1930 * Lookup the interface by its MAC address and insert the IPv4 address into the cache.
1931 * Delete the old client address first, just in case it changed in a renewal.
1932 */
1933 case RTNET_DHCP_MT_ACK:
1934 if (intnetR0IPv4AddrIsGood(pDhcp->bp_yiaddr))
1935 {
1936 PINTNETIF pMatchingIf = NULL;
1937 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1938 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1939
1940 uint32_t iIf = pNetwork->MacTab.cEntries;
1941 while (iIf-- > 0)
1942 {
1943 PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
1944 if ( intnetR0IfHasMacAddr(pCur)
1945 && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
1946 {
1947 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1948 (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
1949 if (!pMatchingIf)
1950 {
1951 pMatchingIf = pCur;
1952 intnetR0BusyIncIf(pMatchingIf);
1953 }
1954 }
1955 }
1956
1957 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1958
1959 if (pMatchingIf)
1960 {
1961 intnetR0IfAddrCacheAdd(pMatchingIf, &pMatchingIf->aAddrCache[kIntNetAddrType_IPv4],
1962 (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
1963 intnetR0BusyDecIf(pMatchingIf);
1964 }
1965 }
1966 return;
1967
1968
1969 /*
1970 * Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
1971 */
1972 case RTNET_DHCP_MT_RELEASE:
1973 {
1974 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
1975 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
1976
1977 uint32_t iIf = pNetwork->MacTab.cEntries;
1978 while (iIf-- > 0)
1979 {
1980 PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
1981 if ( intnetR0IfHasMacAddr(pCur)
1982 && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
1983 {
1984 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1985 (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
1986 intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
1987 (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
1988 }
1989 }
1990
1991 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
1992 break;
1993 }
1994 }
1995
1996}
1997
1998
1999/**
2000 * Worker for intnetR0TrunkIfSnoopAddr that takes care of what
2001 * is likely to be a DHCP message.
2002 *
2003 * The caller has already check that the UDP source and destination ports
2004 * are BOOTPS or BOOTPC.
2005 *
2006 * @param pNetwork The network this frame was seen on.
2007 * @param pSG The gather list for the frame.
2008 */
2009static void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
2010{
2011 /*
2012 * Get a pointer to a linear copy of the full packet, using the
2013 * temporary buffer if necessary.
2014 */
2015 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
2016 uint32_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
2017 if (pSG->cSegsUsed > 1)
2018 {
2019 cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
2020 Log6(("intnetR0TrunkIfSnoopDhcp: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
2021 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
2022 return;
2023 //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
2024 pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
2025 }
2026
2027 /*
2028 * Validate the IP header and find the UDP packet.
2029 */
2030 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fChecksum*/))
2031 {
2032 Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
2033 return;
2034 }
2035 uint32_t cbIpHdr = pIpHdr->ip_hl * 4;
2036
2037 /*
2038 * Hand it over to the common DHCP snooper.
2039 */
2040 intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
2041}
2042
2043#endif /* INTNET_WITH_DHCP_SNOOPING */
2044
2045
2046/**
2047 * Snoops up source addresses from ARP requests and purge these from the address
2048 * caches.
2049 *
2050 * The purpose of this purging is to get rid of stale addresses.
2051 *
2052 * @param pNetwork The network this frame was seen on.
2053 * @param pSG The gather list for the frame.
2054 */
2055static void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
2056{
2057 /*
2058 * Check the minimum size first.
2059 */
2060 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
2061 return;
2062
2063 /*
2064 * Copy to temporary buffer if necessary.
2065 */
2066 uint32_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
2067 PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
2068 if ( pSG->cSegsUsed != 1
2069 && pSG->aSegs[0].cb < cbPacket)
2070 {
2071 if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
2072 != (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
2073 && !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
2074 return;
2075 pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
2076 }
2077
2078 /*
2079 * Ignore packets which doesn't interest us or we perceive as malformed.
2080 */
2081 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
2082 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
2083 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
2084 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
2085 return;
2086 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
2087 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
2088 && ar_oper != RTNET_ARPOP_REPLY))
2089 {
2090 Log6(("ts-ar: op=%#x\n", ar_oper));
2091 return;
2092 }
2093
2094 /*
2095 * Delete the source address if it's OK.
2096 */
2097 if ( !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_sha)
2098 && ( pArpIPv4->ar_sha.au16[0]
2099 || pArpIPv4->ar_sha.au16[1]
2100 || pArpIPv4->ar_sha.au16[2])
2101 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
2102 {
2103 Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
2104 pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
2105 intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
2106 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
2107 }
2108}
2109
2110
2111#ifdef INTNET_WITH_DHCP_SNOOPING
2112/**
2113 * Snoop up addresses from ARP and DHCP traffic from frames comming
2114 * over the trunk connection.
2115 *
2116 * The caller is responsible for do some basic filtering before calling
2117 * this function.
2118 * For IPv4 this means checking against the minimum DHCPv4 frame size.
2119 *
2120 * @param pNetwork The network.
2121 * @param pSG The SG list for the frame.
2122 * @param EtherType The Ethertype of the frame.
2123 */
2124static void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
2125{
2126 switch (EtherType)
2127 {
2128 case RTNET_ETHERTYPE_IPV4:
2129 {
2130 uint32_t cbIpHdr;
2131 uint8_t b;
2132
2133 Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
2134 if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN)
2135 {
2136 /* check if the protocol is UDP */
2137 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
2138 if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
2139 return;
2140
2141 /* get the TCP header length */
2142 cbIpHdr = pIpHdr->ip_hl * 4;
2143 }
2144 else
2145 {
2146 /* check if the protocol is UDP */
2147 if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
2148 != RTNETIPV4_PROT_UDP)
2149 return;
2150
2151 /* get the TCP header length */
2152 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
2153 cbIpHdr = (b & 0x0f) * 4;
2154 }
2155 if (cbIpHdr < RTNETIPV4_MIN_LEN)
2156 return;
2157
2158 /* compare the ports. */
2159 if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + cbIpHdr + RTNETUDP_MIN_LEN)
2160 {
2161 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
2162 if ( ( RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPS
2163 && RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS)
2164 || ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPC
2165 && RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC))
2166 return;
2167 }
2168 else
2169 {
2170 /* get the lower byte of the UDP source port number. */
2171 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
2172 if ( b != RTNETIPV4_PORT_BOOTPS
2173 && b != RTNETIPV4_PORT_BOOTPC)
2174 return;
2175 uint8_t SrcPort = b;
2176 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
2177 if (b)
2178 return;
2179
2180 /* get the lower byte of the UDP destination port number. */
2181 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport) + 1);
2182 if ( b != RTNETIPV4_PORT_BOOTPS
2183 && b != RTNETIPV4_PORT_BOOTPC)
2184 return;
2185 if (b == SrcPort)
2186 return;
2187 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport));
2188 if (b)
2189 return;
2190 }
2191 intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
2192 break;
2193 }
2194
2195 case RTNET_ETHERTYPE_IPV6:
2196 {
2197 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
2198 * need to be edited. Check out how NDP works... */
2199 break;
2200 }
2201
2202 case RTNET_ETHERTYPE_ARP:
2203 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
2204 break;
2205 }
2206}
2207#endif /* INTNET_WITH_DHCP_SNOOPING */
2208
2209
2210/**
2211 * Deals with an IPv4 packet.
2212 *
2213 * This will fish out the source IP address and add it to the cache.
2214 * Then it will look for DHCPRELEASE requests (?) and anything else
2215 * that we migh find useful later.
2216 *
2217 * @param pIf The interface that's sending the frame.
2218 * @param pIpHdr Pointer to the IPv4 header in the frame.
2219 * @param cbPacket The size of the packet, or more correctly the
2220 * size of the frame without the ethernet header.
2221 * @param fGso Set if this is a GSO frame, clear if regular.
2222 */
2223static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket, bool fGso)
2224{
2225 /*
2226 * Check the header size first to prevent access invalid data.
2227 */
2228 if (cbPacket < RTNETIPV4_MIN_LEN)
2229 return;
2230 uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
2231 if ( cbHdr < RTNETIPV4_MIN_LEN
2232 || cbPacket < cbHdr)
2233 return;
2234
2235 /*
2236 * If the source address is good (not broadcast or my network) and
2237 * not already in the address cache of the sender, add it. Validate
2238 * the IP header before adding it.
2239 */
2240 bool fValidatedIpHdr = false;
2241 RTNETADDRU Addr;
2242 Addr.IPv4 = pIpHdr->ip_src;
2243 if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
2244 && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
2245 {
2246 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
2247 {
2248 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
2249 return;
2250 }
2251 intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
2252 fValidatedIpHdr = true;
2253 }
2254
2255#ifdef INTNET_WITH_DHCP_SNOOPING
2256 /*
2257 * Check for potential DHCP packets.
2258 */
2259 if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
2260 && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN /* Min DHCP packet len. */
2261 && !fGso) /* GSO is not applicable to DHCP traffic. */
2262 {
2263 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
2264 if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
2265 || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
2266 && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
2267 || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
2268 {
2269 if ( fValidatedIpHdr
2270 || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
2271 intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
2272 else
2273 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
2274 }
2275 }
2276#endif /* INTNET_WITH_DHCP_SNOOPING */
2277}
2278
2279
2280/**
2281 * Snoop up source addresses from an ARP request or reply.
2282 *
2283 * @param pIf The interface that's sending the frame.
2284 * @param pHdr The ARP header.
2285 * @param cbPacket The size of the packet (migth be larger than the ARP
2286 * request 'cause of min ethernet frame size).
2287 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
2288 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
2289 */
2290static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
2291{
2292 /*
2293 * Ignore packets which doesn't interest us or we perceive as malformed.
2294 */
2295 if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
2296 return;
2297 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
2298 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
2299 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
2300 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
2301 return;
2302 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
2303 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
2304 && ar_oper != RTNET_ARPOP_REPLY))
2305 {
2306 Log6(("ar_oper=%#x\n", ar_oper));
2307 return;
2308 }
2309
2310 /*
2311 * Tag the SG as ARP IPv4 for later editing, then check for addresses
2312 * which can be removed or added to the address cache of the sender.
2313 */
2314 *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
2315
2316 if ( ar_oper == RTNET_ARPOP_REPLY
2317 && !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_tha)
2318 && ( pArpIPv4->ar_tha.au16[0]
2319 || pArpIPv4->ar_tha.au16[1]
2320 || pArpIPv4->ar_tha.au16[2])
2321 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
2322 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
2323 (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
2324
2325 if ( !memcmp(&pArpIPv4->ar_sha, &pIf->MacAddr, sizeof(RTMAC))
2326 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
2327 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
2328 (PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
2329}
2330
2331
2332
2333/**
2334 * Checks packets send by a normal interface for new network
2335 * layer addresses.
2336 *
2337 * @param pIf The interface that's sending the frame.
2338 * @param pbFrame The frame.
2339 * @param cbFrame The size of the frame.
2340 * @param fGso Set if this is a GSO frame, clear if regular.
2341 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
2342 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
2343 */
2344static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, bool fGso, uint16_t *pfSgFlags)
2345{
2346 /*
2347 * Fish out the ethertype and look for stuff we can handle.
2348 */
2349 if (cbFrame <= sizeof(RTNETETHERHDR))
2350 return;
2351 cbFrame -= sizeof(RTNETETHERHDR);
2352
2353 uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
2354 switch (EtherType)
2355 {
2356 case RTNET_ETHERTYPE_IPV4:
2357 intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso);
2358 break;
2359#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
2360 case RTNET_ETHERTYPE_IPV6:
2361 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
2362 * need to be edited. Check out how NDP works... */
2363 intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso, pfSgFlags);
2364 break;
2365#endif
2366#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
2367 case RTNET_ETHERTYPE_IPX_1:
2368 case RTNET_ETHERTYPE_IPX_2:
2369 case RTNET_ETHERTYPE_IPX_3:
2370 intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
2371 break;
2372#endif
2373 case RTNET_ETHERTYPE_ARP:
2374 intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
2375 break;
2376 }
2377}
2378
2379
2380/**
2381 * Writes a frame packet to the ring buffer.
2382 *
2383 * @returns VBox status code.
2384 * @param pBuf The buffer.
2385 * @param pRingBuf The ring buffer to read from.
2386 * @param pSG The gather list.
2387 * @param pNewDstMac Set the destination MAC address to the address if specified.
2388 */
2389static int intnetR0RingWriteFrame(PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
2390{
2391 PINTNETHDR pHdr = NULL; /* shut up gcc*/
2392 void *pvDst = NULL; /* ditto */
2393 int rc;
2394 if (pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
2395 rc = IntNetRingAllocateFrame(pRingBuf, pSG->cbTotal, &pHdr, &pvDst);
2396 else
2397 rc = IntNetRingAllocateGsoFrame(pRingBuf, pSG->cbTotal, &pSG->GsoCtx, &pHdr, &pvDst);
2398 if (RT_SUCCESS(rc))
2399 {
2400 IntNetSgRead(pSG, pvDst);
2401 if (pNewDstMac)
2402 ((PRTNETETHERHDR)pvDst)->DstMac = *pNewDstMac;
2403
2404 IntNetRingCommitFrame(pRingBuf, pHdr);
2405 return VINF_SUCCESS;
2406 }
2407 return rc;
2408}
2409
2410
2411/**
2412 * Sends a frame to a specific interface.
2413 *
2414 * @param pIf The interface.
2415 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2416 * @param pSG The gather buffer which data is being sent to the interface.
2417 * @param pNewDstMac Set the destination MAC address to the address if specified.
2418 */
2419static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
2420{
2421 /*
2422 * Grab the receive/producer lock and copy over the frame.
2423 */
2424 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
2425 RTSpinlockAcquireNoInts(pIf->hRecvInSpinlock, &Tmp);
2426 int rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
2427 RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock, &Tmp);
2428 if (RT_SUCCESS(rc))
2429 {
2430 pIf->cYields = 0;
2431 RTSemEventSignal(pIf->hRecvEvent);
2432 return;
2433 }
2434
2435 Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
2436
2437 /*
2438 * Scheduling hack, for unicore machines primarily.
2439 */
2440 if ( pIf->fActive
2441 && pIf->cYields < 4 /* just twice */
2442 && pIfSender /* but not if it's from the trunk */
2443 && RTThreadPreemptIsEnabled(NIL_RTTHREAD)
2444 )
2445 {
2446 unsigned cYields = 2;
2447 while (--cYields > 0)
2448 {
2449 RTSemEventSignal(pIf->hRecvEvent);
2450 RTThreadYield();
2451
2452 RTSpinlockAcquireNoInts(pIf->hRecvInSpinlock, &Tmp);
2453 rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
2454 RTSpinlockReleaseNoInts(pIf->hRecvInSpinlock, &Tmp);
2455 if (RT_SUCCESS(rc))
2456 {
2457 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
2458 RTSemEventSignal(pIf->hRecvEvent);
2459 return;
2460 }
2461 pIf->cYields++;
2462 }
2463 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
2464 }
2465
2466 /* ok, the frame is lost. */
2467 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
2468 RTSemEventSignal(pIf->hRecvEvent);
2469}
2470
2471
2472/**
2473 * Fallback path that does the GSO segmenting before passing the frame on to the
2474 * trunk interface.
2475 *
2476 * The caller holds the trunk lock.
2477 *
2478 * @param pThis The trunk.
2479 * @param pIfSender The IF sending the frame.
2480 * @param pSG Pointer to the gather list.
2481 * @param fDst The destination flags.
2482 */
2483static int intnetR0TrunkIfSendGsoFallback(PINTNETTRUNKIF pThis, PINTNETIF pIfSender, PINTNETSG pSG, uint32_t fDst)
2484{
2485 /*
2486 * Since we're only using this for GSO frame comming from the internal
2487 * network interfaces and never the trunk, we can assume there is only
2488 * one segment. This simplifies the code quite a bit.
2489 */
2490 Assert(PDMNetGsoIsValid(&pSG->GsoCtx, sizeof(pSG->GsoCtx), pSG->cbTotal));
2491 AssertReturn(pSG->cSegsUsed == 1, VERR_INTERNAL_ERROR_4);
2492
2493 union
2494 {
2495 uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
2496 INTNETSG SG;
2497 } u;
2498
2499 /*
2500 * Carve out the frame segments with the header and frame in different
2501 * scatter / gather segments.
2502 */
2503 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
2504 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
2505 {
2506 uint32_t cbSegPayload;
2507 uint32_t offSegPayload = PDMNetGsoCarveSegment(&pSG->GsoCtx, (uint8_t *)pSG->aSegs[0].pv, pSG->cbTotal, iSeg, cSegs,
2508 pThis->abGsoHdrs, &cbSegPayload);
2509
2510 IntNetSgInitTempSegs(&u.SG, pSG->GsoCtx.cbHdrs + cbSegPayload, 2, 2);
2511 u.SG.aSegs[0].Phys = NIL_RTHCPHYS;
2512 u.SG.aSegs[0].pv = pThis->abGsoHdrs;
2513 u.SG.aSegs[0].cb = pSG->GsoCtx.cbHdrs;
2514 u.SG.aSegs[1].Phys = NIL_RTHCPHYS;
2515 u.SG.aSegs[1].pv = (uint8_t *)pSG->aSegs[0].pv + offSegPayload;
2516 u.SG.aSegs[1].cb = (uint32_t)cbSegPayload;
2517
2518 int rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender, &u.SG, fDst);
2519 if (RT_FAILURE(rc))
2520 return rc;
2521 }
2522 return VINF_SUCCESS;
2523}
2524
2525
2526/**
2527 * Checks if any of the given trunk destinations can handle this kind of GSO SG.
2528 *
2529 * @returns true if it can, false if it cannot.
2530 * @param pThis The trunk.
2531 * @param pSG The scatter / gather buffer.
2532 * @param fDst The destination mask.
2533 */
2534DECLINLINE(bool) intnetR0TrunkIfCanHandleGsoFrame(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
2535{
2536 uint8_t u8Type = pSG->GsoCtx.u8Type;
2537 AssertReturn(u8Type < 32, false); /* paranoia */
2538 uint32_t fMask = RT_BIT_32(u8Type);
2539
2540 if (fDst == INTNETTRUNKDIR_HOST)
2541 return !!(pThis->fHostGsoCapabilites & fMask);
2542 if (fDst == INTNETTRUNKDIR_WIRE)
2543 return !!(pThis->fWireGsoCapabilites & fMask);
2544 Assert(fDst == (INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST));
2545 return !!(pThis->fHostGsoCapabilites & pThis->fWireGsoCapabilites & fMask);
2546}
2547
2548
2549/**
2550 * Sends a frame down the trunk.
2551 *
2552 * @param pThis The trunk.
2553 * @param pNetwork The network the frame is being sent to.
2554 * @param pIfSender The IF sending the frame. Used for MAC address
2555 * checks in shared MAC mode.
2556 * @param fDst The destination flags.
2557 * @param pSG Pointer to the gather list.
2558 */
2559static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
2560 uint32_t fDst, PINTNETSG pSG)
2561{
2562 /*
2563 * Quick sanity check.
2564 */
2565 AssertPtr(pThis);
2566 AssertPtr(pNetwork);
2567 AssertPtr(pSG);
2568 Assert(fDst);
2569 AssertReturnVoid(pThis->pIfPort);
2570
2571 /*
2572 * Edit the frame if we're sharing the MAC address with the host on the wire.
2573 *
2574 * If the frame is headed for both the host and the wire, we'll have to send
2575 * it to the host before making any modifications, and force the OS specific
2576 * backend to copy it. We do this by marking it as TEMP (which is always the
2577 * case right now).
2578 */
2579 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2580 && (fDst & INTNETTRUNKDIR_WIRE))
2581 {
2582 /*
2583 * Dispatch it to the host before making changes.
2584 */
2585 if (fDst & INTNETTRUNKDIR_HOST)
2586 {
2587 Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
2588 intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG);
2589 fDst &= ~INTNETTRUNKDIR_HOST;
2590 }
2591
2592 /*
2593 * Edit the source address so that it it's the same as the host.
2594 */
2595 /* ASSUME frame from IntNetR0IfSend! */
2596 AssertReturnVoid(pSG->cSegsUsed == 1);
2597 AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
2598 AssertReturnVoid(pIfSender);
2599 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
2600
2601 pEthHdr->SrcMac = pThis->MacAddr;
2602
2603 /*
2604 * Deal with tags from the snooping phase.
2605 */
2606 if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
2607 {
2608 /*
2609 * APR IPv4: replace hardware (MAC) addresses because these end up
2610 * in ARP caches. So, if we don't the other machiens will
2611 * send the packets to the MAC address of the guest
2612 * instead of the one of the host, which won't work on
2613 * wireless of course...
2614 */
2615 PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
2616 if (!memcmp(&pArp->ar_sha, &pIfSender->MacAddr, sizeof(RTMAC)))
2617 {
2618 Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->MacAddr));
2619 pArp->ar_sha = pThis->MacAddr;
2620 }
2621 if (!memcmp(&pArp->ar_tha, &pIfSender->MacAddr, sizeof(RTMAC))) /* just in case... */
2622 {
2623 Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->MacAddr));
2624 pArp->ar_tha = pThis->MacAddr;
2625 }
2626 }
2627 //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
2628 //{ /// @todo move the editing into a different function
2629 //}
2630 }
2631
2632 /*
2633 * Send the frame, handling the GSO fallback .
2634 * .
2635 * Note! The trunk implementation will re-check that the trunk is active .
2636 * before sending, so we don't have to duplicate that effort here.
2637 */
2638 int rc;
2639 if ( pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID
2640 || intnetR0TrunkIfCanHandleGsoFrame(pThis, pSG, fDst) )
2641 rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender, pSG, fDst);
2642 else
2643 rc = intnetR0TrunkIfSendGsoFallback(pThis, pIfSender, pSG, fDst);
2644
2645 /** @todo failure statistics? */
2646 Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst)); NOREF(rc);
2647}
2648
2649
2650/**
2651 * Edits an ARP packet arriving from the wire via the trunk connection.
2652 *
2653 * @param pNetwork The network the frame is being sent to.
2654 * @param pSG Pointer to the gather list for the frame.
2655 * The flags and data content may be updated.
2656 * @param pEthHdr Pointer to the ethernet header. This may also be
2657 * updated if it's a unicast...
2658 */
2659static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
2660{
2661 /*
2662 * Check the minimum size and get a linear copy of the thing to work on,
2663 * using the temporary buffer if necessary.
2664 */
2665 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
2666 return;
2667 PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
2668 if ( pSG->cSegsUsed != 1
2669 && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
2670 {
2671 Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
2672 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
2673 return;
2674 pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
2675 pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
2676 }
2677
2678 /*
2679 * Ignore packets which doesn't interest us or we perceive as malformed.
2680 */
2681 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
2682 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
2683 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
2684 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
2685 return;
2686 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
2687 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
2688 && ar_oper != RTNET_ARPOP_REPLY))
2689 {
2690 Log6(("ar_oper=%#x\n", ar_oper));
2691 return;
2692 }
2693
2694 /* Tag it as ARP IPv4. */
2695 pSG->fFlags |= INTNETSG_FLAGS_ARP_IPV4;
2696
2697 /*
2698 * The thing we're interested in here is a reply to a query made by a guest
2699 * since we modified the MAC in the initial request the guest made.
2700 */
2701 if ( ar_oper == RTNET_ARPOP_REPLY
2702 && !memcmp(&pArpIPv4->ar_tha, &pNetwork->MacTab.pTrunk->MacAddr, sizeof(RTMAC)))
2703 {
2704 PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
2705 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
2706 if (pIf)
2707 {
2708 Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->MacAddr));
2709 pArpIPv4->ar_tha = pIf->MacAddr;
2710 if (!memcmp(&pEthHdr->DstMac, &pNetwork->MacTab.pTrunk->MacAddr, sizeof(RTMAC)))
2711 {
2712 Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->MacAddr));
2713 pEthHdr->DstMac = pIf->MacAddr;
2714 if ((void *)pEthHdr != pSG->aSegs[0].pv)
2715 intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->MacAddr);
2716 }
2717 intnetR0BusyDecIf(pIf);
2718
2719 /* Write back the packet if we've been making changes to a buffered copy. */
2720 if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
2721 intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
2722 }
2723 }
2724}
2725
2726
2727/**
2728 * Detects and edits an DHCP packet arriving from the internal net.
2729 *
2730 * @param pNetwork The network the frame is being sent to.
2731 * @param pSG Pointer to the gather list for the frame.
2732 * The flags and data content may be updated.
2733 * @param pEthHdr Pointer to the ethernet header. This may also be
2734 * updated if it's a unicast...
2735 */
2736static void intnetR0NetworkEditDhcpFromIntNet(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
2737{
2738 /*
2739 * Check the minimum size and get a linear copy of the thing to work on,
2740 * using the temporary buffer if necessary.
2741 */
2742 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN))
2743 return;
2744 /*
2745 * Get a pointer to a linear copy of the full packet, using the
2746 * temporary buffer if necessary.
2747 */
2748 PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
2749 uint32_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
2750 if (pSG->cSegsUsed > 1)
2751 {
2752 cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
2753 Log6(("intnetR0NetworkEditDhcpFromIntNet: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
2754 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
2755 return;
2756 //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
2757 pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
2758 }
2759
2760 /*
2761 * Validate the IP header and find the UDP packet.
2762 */
2763 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fCheckSum*/))
2764 {
2765 Log6(("intnetR0NetworkEditDhcpFromIntNet: bad ip header\n"));
2766 return;
2767 }
2768 size_t cbIpHdr = pIpHdr->ip_hl * 4;
2769 if ( pIpHdr->ip_p != RTNETIPV4_PROT_UDP /* DHCP is UDP. */
2770 || cbPacket < cbIpHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
2771 return;
2772
2773 size_t cbUdpPkt = cbPacket - cbIpHdr;
2774 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr);
2775 /* We are only interested in DHCP packets coming from client to server. */
2776 if ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS
2777 || RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC)
2778 return;
2779
2780 /*
2781 * Check if the DHCP message is valid and get the type.
2782 */
2783 if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt, true /*fCheckSum*/))
2784 {
2785 Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad UDP packet\n"));
2786 return;
2787 }
2788 PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
2789 uint8_t MsgType;
2790 if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
2791 {
2792 Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
2793 return;
2794 }
2795
2796 switch (MsgType)
2797 {
2798 case RTNET_DHCP_MT_DISCOVER:
2799 case RTNET_DHCP_MT_REQUEST:
2800 Log6(("intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n", MsgType, pDhcp->bp_flags));
2801 if (!(pDhcp->bp_flags & RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST)))
2802 {
2803 /* Patch flags */
2804 uint16_t uFlags = pDhcp->bp_flags | RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
2805 intnetR0SgWritePart(pSG, (uintptr_t)&pDhcp->bp_flags - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(uFlags), &uFlags);
2806 /* Patch UDP checksum */
2807 uint32_t uChecksum = (uint32_t)~pUdpHdr->uh_sum + RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
2808 while (uChecksum >> 16)
2809 uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
2810 uChecksum = ~uChecksum;
2811 intnetR0SgWritePart(pSG, (uintptr_t)&pUdpHdr->uh_sum - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(pUdpHdr->uh_sum), &uChecksum);
2812 }
2813 break;
2814 }
2815}
2816
2817
2818/**
2819 * Checks if the callers context is okay for sending to the specified
2820 * destinations.
2821 *
2822 * @returns true if it's okay, false if it isn't.
2823 * @param pNetwork The network.
2824 * @param pIfSender The interface sending or NULL if it's the trunk.
2825 * @param pDstTab The destination table.
2826 */
2827DECLINLINE(bool) intnetR0NetworkIsContextOk(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCINTNETDSTTAB pDstTab)
2828{
2829 /* Sending to the trunk is the problematic path. If the trunk is the
2830 sender we won't be sending to it, so no problem..
2831 Note! fTrunkDst may be set event if if the trunk is the sender. */
2832 if (!pIfSender)
2833 return true;
2834
2835 uint32_t const fTrunkDst = pDstTab->fTrunkDst;
2836 if (!fTrunkDst)
2837 return true;
2838
2839 /* ASSUMES: that the trunk won't change its report while we're checking. */
2840 PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
2841 if ((fTrunkDst & pTrunk->fNoPreemptDsts) == fTrunkDst)
2842 return true;
2843
2844 /* ASSUMES: That a preemption test detects HWACCM contexts. (Will work on
2845 non-preemptive systems as well.) */
2846 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
2847 return true;
2848 return false;
2849}
2850
2851
2852/**
2853 * Checks if the callers context is okay for doing a broadcast given the
2854 * specified source.
2855 *
2856 * @returns true if it's okay, false if it isn't.
2857 * @param pNetwork The network.
2858 * @param fSrc The source of the packet. (0 (intnet),
2859 * INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
2860 */
2861DECLINLINE(bool) intnetR0NetworkIsContextOkForBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc)
2862{
2863 /* Sending to the trunk is the problematic path. If the trunk is the
2864 sender we won't be sending to it, so no problem. */
2865 if (fSrc)
2866 return true;
2867
2868 /* ASSUMES: That a preemption test detects HWACCM contexts. (Will work on
2869 non-preemptive systems as well.) */
2870 if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
2871 return true;
2872
2873 /* PARANOIA: Grab the spinlock to make sure the trunk structure cannot be
2874 freed while we're touching it. */
2875 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
2876 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
2877 PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
2878
2879 bool fRc = !pTrunk
2880 || pTrunk->fNoPreemptDsts == (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
2881 || ( (!pNetwork->MacTab.fHostActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_HOST) )
2882 && (!pNetwork->MacTab.fWireActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_WIRE) ) );
2883
2884 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
2885
2886 return fRc;
2887}
2888
2889
2890/**
2891 * Check context, edit, snoop and switch a broadcast frame when sharing MAC
2892 * address on the wire.
2893 *
2894 * The caller must hold at least one interface on the network busy to prevent it
2895 * from destructing beath us.
2896 *
2897 * @param pNetwork The network the frame is being sent to.
2898 * @param fSrc The source of the packet. (0 (intnet),
2899 * INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
2900 * @param pIfSender The sender interface, NULL if trunk. Used to
2901 * prevent sending an echo to the sender.
2902 * @param pSG Pointer to the gather list.
2903 * @param pEthHdr Pointer to the ethernet header.
2904 * @param pDstTab The destination output table.
2905 */
2906static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchBroadcast(PINTNETNETWORK pNetwork,
2907 uint32_t fSrc, PINTNETIF pIfSender,
2908 PINTNETSG pSG, PRTNETETHERHDR pEthHdr,
2909 PINTNETDSTTAB pDstTab)
2910{
2911 /*
2912 * Before doing any work here, we need to figure out if we can handle it
2913 * in the current context. The restrictions are solely on the trunk.
2914 *
2915 * Note! Since at least one interface is busy, there won't be any changes
2916 * to the parameters here (unless the trunk changes its capability
2917 * report, which it shouldn't).
2918 */
2919 if (!intnetR0NetworkIsContextOkForBroadcast(pNetwork, fSrc))
2920 return INTNETSWDECISION_BAD_CONTEXT;
2921
2922 /*
2923 * Check for ARP packets from the wire since we'll have to make
2924 * modification to them if we're sharing the MAC address with the host.
2925 */
2926 if ( (fSrc & INTNETTRUNKDIR_WIRE)
2927 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP
2928 && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
2929 intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
2930
2931 /*
2932 * Check for DHCP packets from the internal net since we'll have to set
2933 * broadcast flag in DHCP requests if we're sharing the MAC address with
2934 * the host. GSO is not applicable to DHCP traffic.
2935 */
2936 if ( !fSrc
2937 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV4
2938 && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
2939 intnetR0NetworkEditDhcpFromIntNet(pNetwork, pSG, pEthHdr);
2940
2941 /*
2942 * Snoop address info from packet orginating from the trunk connection.
2943 */
2944 if (fSrc)
2945 {
2946#ifdef INTNET_WITH_DHCP_SNOOPING
2947 uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
2948 if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
2949 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
2950 && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID )
2951 || (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4) )
2952 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
2953#else
2954 if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
2955 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
2956#endif
2957 }
2958
2959 /*
2960 * Create the broadcast destination table.
2961 */
2962 return intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
2963}
2964
2965
2966/**
2967 * Check context, snoop and switch a unicast frame using the network layer
2968 * address of the link layer one (when sharing MAC address on the wire).
2969 *
2970 * This function is only used for frames coming from the wire (trunk).
2971 *
2972 * @returns true if it's addressed to someone on the network, otherwise false.
2973 * @param pNetwork The network the frame is being sent to.
2974 * @param pSG Pointer to the gather list.
2975 * @param pEthHdr Pointer to the ethernet header.
2976 * @param pDstTab The destination output table.
2977 */
2978static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchUnicast(PINTNETNETWORK pNetwork, PINTNETSG pSG,
2979 PRTNETETHERHDR pEthHdr, PINTNETDSTTAB pDstTab)
2980{
2981 /*
2982 * Extract the network address from the packet.
2983 */
2984 RTNETADDRU Addr;
2985 INTNETADDRTYPE enmAddrType;
2986 uint8_t cbAddr;
2987 switch (RT_BE2H_U16(pEthHdr->EtherType))
2988 {
2989 case RTNET_ETHERTYPE_IPV4:
2990 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
2991 {
2992 Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
2993 return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
2994 }
2995 enmAddrType = kIntNetAddrType_IPv4;
2996 cbAddr = sizeof(Addr.IPv4);
2997 Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
2998 break;
2999
3000#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
3001 case RTNET_ETHERTYPE_IPV6
3002 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
3003 {
3004 Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
3005 return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
3006 }
3007 enmAddrType = kIntNetAddrType_IPv6;
3008 cbAddr = sizeof(Addr.IPv6);
3009 break;
3010#endif
3011#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
3012 case RTNET_ETHERTYPE_IPX_1:
3013 case RTNET_ETHERTYPE_IPX_2:
3014 case RTNET_ETHERTYPE_IPX_3:
3015 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
3016 {
3017 Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
3018 return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
3019 }
3020 enmAddrType = kIntNetAddrType_IPX;
3021 cbAddr = sizeof(Addr.IPX);
3022 break;
3023#endif
3024
3025 /*
3026 * Treat ARP as broadcast (it shouldn't end up here normally,
3027 * so it goes last in the switch).
3028 */
3029 case RTNET_ETHERTYPE_ARP:
3030 Log6(("intnetshareduni: ARP\n"));
3031 /** @todo revisit this broadcasting of unicast ARP frames! */
3032 return intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, INTNETTRUNKDIR_WIRE, NULL, pSG, pEthHdr, pDstTab);
3033
3034 /*
3035 * Unknown packets are sent to the trunk and any promiscuous interfaces.
3036 */
3037 default:
3038 {
3039 Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
3040 return intnetR0NetworkSwitchTrunkAndPromisc(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
3041 }
3042 }
3043
3044 /*
3045 * Do level-3 switching.
3046 */
3047 INTNETSWDECISION enmSwDecision = intnetR0NetworkSwitchLevel3(pNetwork, &pEthHdr->DstMac,
3048 enmAddrType, &Addr, cbAddr,
3049 INTNETTRUNKDIR_WIRE, pDstTab);
3050
3051#ifdef INTNET_WITH_DHCP_SNOOPING
3052 /*
3053 * Perform DHCP snooping. GSO is not applicable to DHCP traffic
3054 */
3055 if ( enmAddrType == kIntNetAddrType_IPv4
3056 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
3057 && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
3058 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
3059#endif /* INTNET_WITH_DHCP_SNOOPING */
3060
3061 return enmSwDecision;
3062}
3063
3064
3065/**
3066 * Release all the interfaces in the destination table when we realize that
3067 * we're in a context where we cannot get the job done.
3068 *
3069 * @param pNetwork The network.
3070 * @param pDstTab The destination table.
3071 */
3072static void intnetR0NetworkReleaseDstTab(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab)
3073{
3074 /* The trunk interface. */
3075 if (pDstTab->fTrunkDst)
3076 {
3077 PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
3078 intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
3079 pDstTab->pTrunk = NULL;
3080 pDstTab->fTrunkDst = 0;
3081 }
3082
3083 /* Regular interfaces. */
3084 uint32_t iIf = pDstTab->cIfs;
3085 while (iIf-- > 0)
3086 {
3087 PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
3088 intnetR0BusyDecIf(pIf);
3089 pDstTab->aIfs[iIf].pIf = NULL;
3090 }
3091 pDstTab->cIfs = 0;
3092}
3093
3094
3095/**
3096 * Deliver the frame to the interfaces specified in the destination table.
3097 *
3098 * @param pNetwork The network.
3099 * @param pDstTab The destination table.
3100 * @param pSG The frame to send.
3101 * @param pIfSender The sender interface. NULL if it origined via
3102 * the trunk.
3103 */
3104static void intnetR0NetworkDeliver(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab, PINTNETSG pSG, PINTNETIF pIfSender)
3105{
3106 /*
3107 * Trunk first so we don't wast any more time before hitting the wire.
3108 *
3109 * Note! The switching functions will include the trunk even when the frame
3110 * source is the trunk. This is because we need it to figure out
3111 * whether the other half of the trunk should see the frame or not
3112 * and let the caller know.
3113 *
3114 * So, we'll ignore trunk sends here if the frame origin is
3115 * INTNETTRUNKSWPORT::pfnRecv.
3116 */
3117 if (pDstTab->fTrunkDst)
3118 {
3119 PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
3120 if (pIfSender)
3121 intnetR0TrunkIfSend(pTrunk, pNetwork, pIfSender, pDstTab->fTrunkDst, pSG);
3122 intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
3123 pDstTab->pTrunk = NULL;
3124 pDstTab->fTrunkDst = 0;
3125 }
3126
3127 /*
3128 * Do the interfaces.
3129 */
3130 uint32_t iIf = pDstTab->cIfs;
3131 while (iIf-- > 0)
3132 {
3133 PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
3134 intnetR0IfSend(pIf, pIfSender, pSG,
3135 pDstTab->aIfs[iIf].fReplaceDstMac ? &pIf->MacAddr: NULL);
3136 intnetR0BusyDecIf(pIf);
3137 pDstTab->aIfs[iIf].pIf = NULL;
3138 }
3139 pDstTab->cIfs = 0;
3140}
3141
3142
3143/**
3144 * Sends a frame.
3145 *
3146 * This function will distribute the frame to the interfaces it is addressed to.
3147 * It will also update the MAC address of the sender.
3148 *
3149 * The caller must own the network mutex.
3150 *
3151 * @returns The switching decision.
3152 * @param pNetwork The network the frame is being sent to.
3153 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
3154 * @param fSrc The source flags. This 0 if it's not from the trunk.
3155 * @param pSG Pointer to the gather list.
3156 * @param pDstTab The destination table to use.
3157 */
3158static INTNETSWDECISION intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
3159 PINTNETSG pSG, PINTNETDSTTAB pDstTab)
3160{
3161 /*
3162 * Assert reality.
3163 */
3164 AssertPtr(pNetwork);
3165 AssertPtrNull(pIfSender);
3166 Assert(pIfSender ? fSrc == 0 : fSrc != 0);
3167 Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
3168 AssertPtr(pSG);
3169 Assert(pSG->cSegsUsed >= 1);
3170 Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
3171 if (pSG->cbTotal < sizeof(RTNETETHERHDR))
3172 return INTNETSWDECISION_INVALID;
3173
3174 /*
3175 * Get the ethernet header (might theoretically involve multiple segments).
3176 */
3177 RTNETETHERHDR EthHdr;
3178 if (pSG->aSegs[0].cb >= sizeof(EthHdr))
3179 EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
3180 else if (!intnetR0SgReadPart(pSG, 0, sizeof(EthHdr), &EthHdr))
3181 return INTNETSWDECISION_INVALID;
3182 if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
3183 || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
3184 || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
3185 || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
3186 || EthHdr.DstMac.au8[0] == 0xff
3187 || EthHdr.SrcMac.au8[0] == 0xff)
3188 Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
3189 &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
3190
3191 /*
3192 * Learn the MAC address of the sender. No re-learning as the interface
3193 * user will normally tell us the right MAC address.
3194 *
3195 * Note! We don't notify the trunk about these mainly because of the
3196 * problematic contexts we might be called in.
3197 */
3198 if (RT_UNLIKELY( pIfSender
3199 && !pIfSender->fMacSet
3200 && memcmp(&EthHdr.SrcMac, &pIfSender->MacAddr, sizeof(pIfSender->MacAddr))
3201 && !intnetR0IsMacAddrMulticast(&EthHdr.SrcMac)
3202 ))
3203 {
3204 Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->MacAddr, &EthHdr.SrcMac));
3205 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
3206 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
3207
3208 PINTNETMACTABENTRY pIfEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIfSender);
3209 if (pIfEntry)
3210 pIfEntry->MacAddr = EthHdr.SrcMac;
3211 pIfSender->MacAddr = EthHdr.SrcMac;
3212
3213 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
3214 }
3215
3216 /*
3217 * Deal with MAC address sharing as that may required editing of the
3218 * packets before we dispatch them anywhere.
3219 */
3220 INTNETSWDECISION enmSwDecision;
3221 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3222 {
3223 if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
3224 enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, fSrc, pIfSender, pSG, &EthHdr, pDstTab);
3225 else if (fSrc & INTNETTRUNKDIR_WIRE)
3226 enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchUnicast(pNetwork, pSG, &EthHdr, pDstTab);
3227 else
3228 enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
3229 }
3230 else if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
3231 enmSwDecision = intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
3232 else
3233 enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
3234
3235 /*
3236 * Deliver to the destinations if we can.
3237 */
3238 if (enmSwDecision != INTNETSWDECISION_BAD_CONTEXT)
3239 {
3240 if (intnetR0NetworkIsContextOk(pNetwork, pIfSender, pDstTab))
3241 intnetR0NetworkDeliver(pNetwork, pDstTab, pSG, pIfSender);
3242 else
3243 {
3244 intnetR0NetworkReleaseDstTab(pNetwork, pDstTab);
3245 enmSwDecision = INTNETSWDECISION_BAD_CONTEXT;
3246 }
3247 }
3248
3249 return enmSwDecision;
3250}
3251
3252
3253/**
3254 * Sends one or more frames.
3255 *
3256 * The function will first the frame which is passed as the optional arguments
3257 * pvFrame and cbFrame. These are optional since it also possible to chain
3258 * together one or more frames in the send buffer which the function will
3259 * process after considering it's arguments.
3260 *
3261 * The caller is responsible for making sure that there are no concurrent calls
3262 * to this method (with the same handle).
3263 *
3264 * @returns VBox status code.
3265 * @param hIf The interface handle.
3266 * @param pSession The caller's session.
3267 */
3268INTNETR0DECL(int) IntNetR0IfSend(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
3269{
3270 Log5(("IntNetR0IfSend: hIf=%RX32\n", hIf));
3271
3272 /*
3273 * Validate input and translate the handle.
3274 */
3275 PINTNET pIntNet = g_pIntNet;
3276 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3277 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3278
3279 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3280 if (!pIf)
3281 return VERR_INVALID_HANDLE;
3282
3283 /*
3284 * Make sure we've got a network.
3285 */
3286 int rc = VINF_SUCCESS;
3287 intnetR0BusyIncIf(pIf);
3288 PINTNETNETWORK pNetwork = pIf->pNetwork;
3289 if (RT_LIKELY(pNetwork))
3290 {
3291 /*
3292 * Grab the destination table.
3293 */
3294 PINTNETDSTTAB pDstTab = (PINTNETDSTTAB)ASMAtomicXchgPtr((void * volatile *)&pIf->pDstTab, NULL);
3295 if (RT_LIKELY(pDstTab))
3296 {
3297 /*
3298 * Process the send buffer.
3299 */
3300 INTNETSWDECISION enmSwDecision = INTNETSWDECISION_BROADCAST;
3301 INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
3302 * with buffer sharing for some OS or service. Darwin copies everything so
3303 * I won't bother allocating and managing SGs rigth now. Sorry. */
3304 PINTNETHDR pHdr;
3305 while ((pHdr = IntNetRingGetNextFrameToRead(&pIf->pIntBuf->Send)) != NULL)
3306 {
3307 uint16_t const u16Type = pHdr->u16Type;
3308 if (u16Type == INTNETHDR_TYPE_FRAME)
3309 {
3310 /* Send regular frame. */
3311 void *pvCurFrame = IntNetHdrGetFramePtr(pHdr, pIf->pIntBuf);
3312 IntNetSgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
3313 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3314 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, false /*fGso*/, (uint16_t *)&Sg.fFlags);
3315 enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
3316 }
3317 else if (u16Type == INTNETHDR_TYPE_GSO)
3318 {
3319 /* Send GSO frame if sane. */
3320 PPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pIf->pIntBuf);
3321 uint32_t cbFrame = pHdr->cbFrame - sizeof(*pGso);
3322 if (RT_LIKELY(PDMNetGsoIsValid(pGso, pHdr->cbFrame, cbFrame)))
3323 {
3324 void *pvCurFrame = pGso + 1;
3325 IntNetSgInitTempGso(&Sg, pvCurFrame, cbFrame, pGso);
3326 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3327 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, cbFrame, true /*fGso*/, (uint16_t *)&Sg.fFlags);
3328 enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
3329 }
3330 else
3331 {
3332 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
3333 enmSwDecision = INTNETSWDECISION_DROP;
3334 }
3335 }
3336 /* Unless it's a padding frame, we're getting babble from the producer. */
3337 else
3338 {
3339 if (u16Type != INTNETHDR_TYPE_PADDING)
3340 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
3341 enmSwDecision = INTNETSWDECISION_DROP;
3342 }
3343 if (enmSwDecision == INTNETSWDECISION_BAD_CONTEXT)
3344 {
3345 rc = VERR_TRY_AGAIN;
3346 break;
3347 }
3348
3349 /* Skip to the next frame. */
3350 IntNetRingSkipFrame(&pIf->pIntBuf->Send);
3351 }
3352
3353 /*
3354 * Put back the destination table.
3355 */
3356 Assert(!pIf->pDstTab);
3357 ASMAtomicWritePtr((void * volatile *)&pIf->pDstTab, pDstTab);
3358 }
3359 else
3360 rc = VERR_INTERNAL_ERROR_4;
3361 }
3362 else
3363 rc = VERR_INTERNAL_ERROR_3;
3364
3365 /*
3366 * Release the interface.
3367 */
3368 intnetR0BusyDecIf(pIf);
3369 intnetR0IfRelease(pIf, pSession);
3370 return rc;
3371}
3372
3373
3374/**
3375 * VMMR0 request wrapper for IntNetR0IfSend.
3376 *
3377 * @returns see IntNetR0IfSend.
3378 * @param pSession The caller's session.
3379 * @param pReq The request packet.
3380 */
3381INTNETR0DECL(int) IntNetR0IfSendReq(PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
3382{
3383 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3384 return VERR_INVALID_PARAMETER;
3385 return IntNetR0IfSend(pReq->hIf, pSession);
3386}
3387
3388
3389/**
3390 * Maps the default buffer into ring 3.
3391 *
3392 * @returns VBox status code.
3393 * @param hIf The interface handle.
3394 * @param pSession The caller's session.
3395 * @param ppRing3Buf Where to store the address of the ring-3 mapping
3396 * (optional).
3397 * @param ppRing0Buf Where to store the address of the ring-0 mapping
3398 * (optional).
3399 */
3400INTNETR0DECL(int) IntNetR0IfGetBufferPtrs(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession,
3401 R3PTRTYPE(PINTNETBUF) *ppRing3Buf, R0PTRTYPE(PINTNETBUF) *ppRing0Buf)
3402{
3403 LogFlow(("IntNetR0IfGetBufferPtrs: hIf=%RX32 ppRing3Buf=%p ppRing0Buf=%p\n", hIf, ppRing3Buf, ppRing0Buf));
3404
3405 /*
3406 * Validate input.
3407 */
3408 PINTNET pIntNet = g_pIntNet;
3409 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3410 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3411
3412 AssertPtrNullReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
3413 AssertPtrNullReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
3414 if (ppRing3Buf)
3415 *ppRing3Buf = 0;
3416 if (ppRing0Buf)
3417 *ppRing0Buf = 0;
3418
3419 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3420 if (!pIf)
3421 return VERR_INVALID_HANDLE;
3422
3423 /*
3424 * ASSUMES that only the process that created an interface can use it.
3425 * ASSUMES that we created the ring-3 mapping when selecting or
3426 * allocating the buffer.
3427 */
3428 int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
3429 if (RT_SUCCESS(rc))
3430 {
3431 if (ppRing3Buf)
3432 *ppRing3Buf = pIf->pIntBufR3;
3433 if (ppRing0Buf)
3434 *ppRing0Buf = (R0PTRTYPE(PINTNETBUF))pIf->pIntBuf; /* tstIntNetR0 mess */
3435
3436 rc = RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
3437 }
3438
3439 intnetR0IfRelease(pIf, pSession);
3440 LogFlow(("IntNetR0IfGetBufferPtrs: returns %Rrc *ppRing3Buf=%p *ppRing0Buf=%p\n",
3441 rc, ppRing3Buf ? *ppRing3Buf : NULL, ppRing0Buf ? *ppRing0Buf : NULL));
3442 return rc;
3443}
3444
3445
3446/**
3447 * VMMR0 request wrapper for IntNetR0IfGetBufferPtrs.
3448 *
3449 * @returns see IntNetR0IfGetRing3Buffer.
3450 * @param pSession The caller's session.
3451 * @param pReq The request packet.
3452 */
3453INTNETR0DECL(int) IntNetR0IfGetBufferPtrsReq(PSUPDRVSESSION pSession, PINTNETIFGETBUFFERPTRSREQ pReq)
3454{
3455 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3456 return VERR_INVALID_PARAMETER;
3457 return IntNetR0IfGetBufferPtrs(pReq->hIf, pSession, &pReq->pRing3Buf, &pReq->pRing0Buf);
3458}
3459
3460
3461#if 0
3462/**
3463 * Gets the physical addresses of the default interface buffer.
3464 *
3465 * @returns VBox status code.
3466 * @param hIF The interface handle.
3467 * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
3468 * @param cPages
3469 */
3470INTNETR0DECL(int) IntNetR0IfGetPhysBuffer(INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
3471{
3472 /*
3473 * Validate input.
3474 */
3475 PINTNET pIntNet = g_pIntNet;
3476 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3477 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3478
3479 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
3480 AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
3481 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3482 if (!pIf)
3483 return VERR_INVALID_HANDLE;
3484
3485 /*
3486 * Grab the lock and get the data.
3487 * ASSUMES that the handle isn't closed while we're here.
3488 */
3489 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
3490 if (RT_SUCCESS(rc))
3491 {
3492 /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
3493 * is no need for any extra bookkeeping here.. */
3494
3495 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
3496 }
3497 intnetR0IfRelease(pIf, pSession);
3498 return VERR_NOT_IMPLEMENTED;
3499}
3500#endif
3501
3502
3503/**
3504 * Sets the promiscuous mode property of an interface.
3505 *
3506 * @returns VBox status code.
3507 * @param hIf The interface handle.
3508 * @param pSession The caller's session.
3509 * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
3510 */
3511INTNETR0DECL(int) IntNetR0IfSetPromiscuousMode(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
3512{
3513 LogFlow(("IntNetR0IfSetPromiscuousMode: hIf=%RX32 fPromiscuous=%d\n", hIf, fPromiscuous));
3514
3515 /*
3516 * Validate & translate input.
3517 */
3518 PINTNET pIntNet = g_pIntNet;
3519 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3520 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3521
3522 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3523 if (!pIf)
3524 {
3525 Log(("IntNetR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
3526 return VERR_INVALID_HANDLE;
3527 }
3528
3529 /*
3530 * Get the network, take the address spinlock, and make the change.
3531 * Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
3532 */
3533 int rc = VINF_SUCCESS;
3534 intnetR0BusyIncIf(pIf);
3535 PINTNETNETWORK pNetwork = pIf->pNetwork;
3536 if (pNetwork)
3537 {
3538 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
3539 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
3540
3541 if (pIf->fPromiscuous != fPromiscuous)
3542 {
3543 Log(("IntNetR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
3544 hIf, !fPromiscuous, !!fPromiscuous));
3545 ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
3546
3547 PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
3548 if (RT_LIKELY(pEntry))
3549 pEntry->fPromiscuous = fPromiscuous;
3550 pIf->fPromiscuous = fPromiscuous;
3551 }
3552
3553 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
3554 }
3555 else
3556 rc = VERR_WRONG_ORDER;
3557
3558 intnetR0BusyDecIf(pIf);
3559 intnetR0IfRelease(pIf, pSession);
3560 return rc;
3561}
3562
3563
3564/**
3565 * VMMR0 request wrapper for IntNetR0IfSetPromiscuousMode.
3566 *
3567 * @returns see IntNetR0IfSetPromiscuousMode.
3568 * @param pSession The caller's session.
3569 * @param pReq The request packet.
3570 */
3571INTNETR0DECL(int) IntNetR0IfSetPromiscuousModeReq(PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
3572{
3573 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3574 return VERR_INVALID_PARAMETER;
3575 return IntNetR0IfSetPromiscuousMode(pReq->hIf, pSession, pReq->fPromiscuous);
3576}
3577
3578
3579/**
3580 * Sets the MAC address of an interface.
3581 *
3582 * @returns VBox status code.
3583 * @param hIf The interface handle.
3584 * @param pSession The caller's session.
3585 * @param pMAC The new MAC address.
3586 */
3587INTNETR0DECL(int) IntNetR0IfSetMacAddress(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
3588{
3589 LogFlow(("IntNetR0IfSetMacAddress: hIf=%RX32 pMac=%p:{%.6Rhxs}\n", hIf, pMac, pMac));
3590
3591 /*
3592 * Validate & translate input.
3593 */
3594 PINTNET pIntNet = g_pIntNet;
3595 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3596 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3597
3598 AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
3599 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3600 if (!pIf)
3601 {
3602 Log(("IntNetR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
3603 return VERR_INVALID_HANDLE;
3604 }
3605
3606 /*
3607 * Get the network, take the address spinlock, and make the change.
3608 * Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
3609 */
3610 int rc = VINF_SUCCESS;
3611 intnetR0BusyIncIf(pIf);
3612 PINTNETNETWORK pNetwork = pIf->pNetwork;
3613 if (pNetwork)
3614 {
3615 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
3616 PINTNETTRUNKIF pTrunk = NULL;
3617
3618 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
3619
3620 if (memcmp(&pIf->MacAddr, pMac, sizeof(pIf->MacAddr)))
3621 {
3622 Log(("IntNetR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
3623 hIf, &pIf->MacAddr, pMac));
3624
3625 /* Update the two copies. */
3626 PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
3627 if (RT_LIKELY(pEntry))
3628 pEntry->MacAddr = *pMac;
3629 pIf->MacAddr = *pMac;
3630 pIf->fMacSet = true;
3631
3632 /* Grab a busy reference to the trunk so we release the lock before notifying it. */
3633 pTrunk = pNetwork->MacTab.pTrunk;
3634 if (pTrunk)
3635 intnetR0BusyIncTrunk(pTrunk);
3636 }
3637
3638 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
3639
3640 if (pTrunk)
3641 {
3642 Log(("IntNetR0IfSetMacAddress: pfnNotifyMacAddress hIf=%RX32\n", hIf));
3643 PINTNETTRUNKIFPORT pIfPort = pTrunk->pIfPort;
3644 if (pIfPort)
3645 pIfPort->pfnNotifyMacAddress(pIfPort, pIf->pvIfData, pMac);
3646 intnetR0BusyDecTrunk(pTrunk);
3647 }
3648 }
3649 else
3650 rc = VERR_WRONG_ORDER;
3651
3652 intnetR0BusyDecIf(pIf);
3653 intnetR0IfRelease(pIf, pSession);
3654 return rc;
3655}
3656
3657
3658/**
3659 * VMMR0 request wrapper for IntNetR0IfSetMacAddress.
3660 *
3661 * @returns see IntNetR0IfSetMacAddress.
3662 * @param pSession The caller's session.
3663 * @param pReq The request packet.
3664 */
3665INTNETR0DECL(int) IntNetR0IfSetMacAddressReq(PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
3666{
3667 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3668 return VERR_INVALID_PARAMETER;
3669 return IntNetR0IfSetMacAddress(pReq->hIf, pSession, &pReq->Mac);
3670}
3671
3672
3673/**
3674 * Worker for intnetR0IfSetActive and intnetR0IfDestruct.
3675 *
3676 * This function will update the active interface count on the network and
3677 * activate or deactivate the trunk connection if necessary. Note that in
3678 * order to do this it is necessary to abandond the network semaphore.
3679 *
3680 * @returns VBox status code.
3681 * @param pNetwork The network.
3682 * @param fIf The interface.
3683 * @param fActive What to do.
3684 */
3685static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
3686{
3687 /* quick santiy check */
3688 AssertPtr(pNetwork);
3689 AssertPtr(pIf);
3690
3691 /*
3692 * The address spinlock of the network protects the variables, while the
3693 * big lock protects the calling of pfnSetState. Grab both lock at once
3694 * to save us the extra hazzle.
3695 */
3696 PINTNET pIntNet = pNetwork->pIntNet;
3697 int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
3698 AssertRCReturn(rc, rc);
3699
3700 PINTNETTRUNKIF pTrunk = NULL;
3701 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
3702 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
3703
3704 /*
3705 * Do the update.
3706 */
3707 if (pIf->fActive != fActive)
3708 {
3709 PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
3710 if (RT_LIKELY(pEntry))
3711 {
3712 pEntry->fActive = fActive;
3713 pIf->fActive = fActive;
3714
3715 if (fActive)
3716 {
3717 pNetwork->cActiveIFs++;
3718 if (pNetwork->cActiveIFs == 1)
3719 {
3720 pTrunk = pNetwork->MacTab.pTrunk;
3721 if (pTrunk)
3722 {
3723 pNetwork->MacTab.fHostActive = true;
3724 pNetwork->MacTab.fWireActive = true;
3725 }
3726 }
3727 }
3728 else
3729 {
3730 pNetwork->cActiveIFs--;
3731 if (pNetwork->cActiveIFs == 0)
3732 {
3733 pTrunk = pNetwork->MacTab.pTrunk;
3734 pNetwork->MacTab.fHostActive = false;
3735 pNetwork->MacTab.fWireActive = false;
3736 }
3737 }
3738 }
3739 }
3740
3741 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
3742
3743 /*
3744 * Tell the trunk if necessary.
3745 */
3746 if (pTrunk && pTrunk->pIfPort)
3747 pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, fActive ? INTNETTRUNKIFSTATE_ACTIVE : INTNETTRUNKIFSTATE_INACTIVE);
3748
3749 RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
3750
3751 return VINF_SUCCESS;
3752}
3753
3754
3755/**
3756 * Sets the active property of an interface.
3757 *
3758 * @returns VBox status code.
3759 * @param hIf The interface handle.
3760 * @param pSession The caller's session.
3761 * @param fActive The new state.
3762 */
3763INTNETR0DECL(int) IntNetR0IfSetActive(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
3764{
3765 LogFlow(("IntNetR0IfSetActive: hIf=%RX32 fActive=%RTbool\n", hIf, fActive));
3766
3767 /*
3768 * Validate & translate input.
3769 */
3770 PINTNET pIntNet = g_pIntNet;
3771 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3772 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3773
3774 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3775 if (!pIf)
3776 {
3777 Log(("IntNetR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
3778 return VERR_INVALID_HANDLE;
3779 }
3780
3781 /*
3782 * Hand it to the network since it might involve the trunk and things are
3783 * tricky there wrt to locking order.
3784 *
3785 * Note! We mark the interface busy so the network cannot be removed while
3786 * we're working on it - paranoia strikes again.
3787 */
3788 intnetR0BusyIncIf(pIf);
3789
3790 int rc;
3791 PINTNETNETWORK pNetwork = pIf->pNetwork;
3792 if (pNetwork)
3793 rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
3794 else
3795 rc = VERR_WRONG_ORDER;
3796
3797 intnetR0BusyDecIf(pIf);
3798 intnetR0IfRelease(pIf, pSession);
3799 return rc;
3800}
3801
3802
3803/**
3804 * VMMR0 request wrapper for IntNetR0IfSetActive.
3805 *
3806 * @returns see IntNetR0IfSetActive.
3807 * @param pIntNet The internal networking instance.
3808 * @param pSession The caller's session.
3809 * @param pReq The request packet.
3810 */
3811INTNETR0DECL(int) IntNetR0IfSetActiveReq(PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
3812{
3813 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3814 return VERR_INVALID_PARAMETER;
3815 return IntNetR0IfSetActive(pReq->hIf, pSession, pReq->fActive);
3816}
3817
3818
3819/**
3820 * Wait for the interface to get signaled.
3821 * The interface will be signaled when is put into the receive buffer.
3822 *
3823 * @returns VBox status code.
3824 * @param hIf The interface handle.
3825 * @param pSession The caller's session.
3826 * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
3827 * used if indefinite wait is desired.
3828 */
3829INTNETR0DECL(int) IntNetR0IfWait(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
3830{
3831 Log4(("IntNetR0IfWait: hIf=%RX32 cMillies=%u\n", hIf, cMillies));
3832
3833 /*
3834 * Get and validate essential handles.
3835 */
3836 PINTNET pIntNet = g_pIntNet;
3837 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3838 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3839
3840 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
3841 if (!pIf)
3842 {
3843 Log(("IntNetR0IfWait: returns VERR_INVALID_HANDLE\n"));
3844 return VERR_INVALID_HANDLE;
3845 }
3846 const INTNETIFHANDLE hIfSelf = pIf->hIf;
3847 const RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
3848 if ( hIfSelf != hIf /* paranoia */
3849 && hRecvEvent != NIL_RTSEMEVENT)
3850 {
3851 Log(("IntNetR0IfWait: returns VERR_SEM_DESTROYED\n"));
3852 return VERR_SEM_DESTROYED;
3853 }
3854
3855 /*
3856 * It is tempting to check if there is data to be read here,
3857 * but the problem with such an approach is that it will cause
3858 * one unnecessary supervisor->user->supervisor trip. There is
3859 * already a slight risk for such, so no need to increase it.
3860 */
3861
3862 /*
3863 * Increment the number of waiters before starting the wait.
3864 * Upon wakeup we must assert reality, checking that we're not
3865 * already destroyed or in the process of being destroyed. This
3866 * code must be aligned with the waiting code in intnetR0IfDestruct.
3867 */
3868 ASMAtomicIncU32(&pIf->cSleepers);
3869 int rc = RTSemEventWaitNoResume(hRecvEvent, cMillies);
3870 if (pIf->hRecvEvent == hRecvEvent)
3871 {
3872 ASMAtomicDecU32(&pIf->cSleepers);
3873 if (!pIf->fDestroying)
3874 {
3875 if (intnetR0IfRelease(pIf, pSession))
3876 rc = VERR_SEM_DESTROYED;
3877 }
3878 else
3879 rc = VERR_SEM_DESTROYED;
3880 }
3881 else
3882 rc = VERR_SEM_DESTROYED;
3883 Log4(("IntNetR0IfWait: returns %Rrc\n", rc));
3884 return rc;
3885}
3886
3887
3888/**
3889 * VMMR0 request wrapper for IntNetR0IfWait.
3890 *
3891 * @returns see IntNetR0IfWait.
3892 * @param pSession The caller's session.
3893 * @param pReq The request packet.
3894 */
3895INTNETR0DECL(int) IntNetR0IfWaitReq(PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
3896{
3897 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3898 return VERR_INVALID_PARAMETER;
3899 return IntNetR0IfWait(pReq->hIf, pSession, pReq->cMillies);
3900}
3901
3902
3903/**
3904 * Close an interface.
3905 *
3906 * @returns VBox status code.
3907 * @param pIntNet The instance handle.
3908 * @param hIf The interface handle.
3909 * @param pSession The caller's session.
3910 */
3911INTNETR0DECL(int) IntNetR0IfClose(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
3912{
3913 LogFlow(("IntNetR0IfClose: hIf=%RX32\n", hIf));
3914
3915 /*
3916 * Validate and free the handle.
3917 */
3918 PINTNET pIntNet = g_pIntNet;
3919 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3920 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
3921
3922 PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
3923 if (!pIf)
3924 return VERR_INVALID_HANDLE;
3925
3926 /* Mark the handle as freed so intnetR0IfDestruct won't free it again. */
3927 ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
3928
3929 /*
3930 * Signal the event semaphore to wake up any threads in IntNetR0IfWait
3931 * and give them a moment to get out and release the interface.
3932 */
3933 uint32_t i = pIf->cSleepers;
3934 while (i-- > 0)
3935 {
3936 RTSemEventSignal(pIf->hRecvEvent);
3937 RTThreadYield();
3938 }
3939 RTSemEventSignal(pIf->hRecvEvent);
3940
3941 /*
3942 * Release the references to the interface object (handle + free lookup).
3943 */
3944 void *pvObj = pIf->pvObj;
3945 intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
3946
3947 int rc = SUPR0ObjRelease(pvObj, pSession);
3948 LogFlow(("IntNetR0IfClose: returns %Rrc\n", rc));
3949 return rc;
3950}
3951
3952
3953/**
3954 * VMMR0 request wrapper for IntNetR0IfCloseReq.
3955 *
3956 * @returns see IntNetR0IfClose.
3957 * @param pSession The caller's session.
3958 * @param pReq The request packet.
3959 */
3960INTNETR0DECL(int) IntNetR0IfCloseReq(PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
3961{
3962 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3963 return VERR_INVALID_PARAMETER;
3964 return IntNetR0IfClose(pReq->hIf, pSession);
3965}
3966
3967
3968/**
3969 * Interface destructor callback.
3970 * This is called for reference counted objectes when the count reaches 0.
3971 *
3972 * @param pvObj The object pointer.
3973 * @param pvUser1 Pointer to the interface.
3974 * @param pvUser2 Pointer to the INTNET instance data.
3975 */
3976static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3977{
3978 PINTNETIF pIf = (PINTNETIF)pvUser1;
3979 PINTNET pIntNet = (PINTNET)pvUser2;
3980 Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
3981
3982 /*
3983 * We grab the INTNET create/open/destroy semaphore to make sure nobody is
3984 * adding or removing interface while we're in here. For paranoid reasons
3985 * we also mark the interface as destroyed here so any waiting threads can
3986 * take evasive action (theoretical case).
3987 */
3988 RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
3989 ASMAtomicWriteBool(&pIf->fDestroying, true);
3990
3991 /*
3992 * Delete the interface handle so the object no longer can be used.
3993 * (Can happen if the client didn't close its session.)
3994 */
3995 INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
3996 if (hIf != INTNET_HANDLE_INVALID)
3997 {
3998 void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession); NOREF(pvObj2);
3999 AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
4000 }
4001
4002 /*
4003 * If we've got a network deactivate and detach ourselves from it. Because
4004 * of cleanup order we might have been orphaned by the network destructor.
4005 */
4006 PINTNETNETWORK pNetwork = pIf->pNetwork;
4007 if (pNetwork)
4008 {
4009 /* set inactive. */
4010 intnetR0NetworkSetIfActive(pNetwork, pIf, false /*fActive*/);
4011
4012 /* remove ourselves from the switch table. */
4013 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4014 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4015
4016 uint32_t iIf = pNetwork->MacTab.cEntries;
4017 while (iIf-- > 0)
4018 if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
4019 {
4020 if (iIf + 1 < pNetwork->MacTab.cEntries)
4021 memmove(&pNetwork->MacTab.paEntries[iIf],
4022 &pNetwork->MacTab.paEntries[iIf + 1],
4023 (pNetwork->MacTab.cEntries - iIf - 1) * sizeof(pNetwork->MacTab.paEntries[0]));
4024 pNetwork->MacTab.cEntries--;
4025 break;
4026 }
4027
4028 PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
4029
4030 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4031
4032 /* Notify the trunk about the interface being destroyed. */
4033 if (pTrunk && pTrunk->pIfPort)
4034 pTrunk->pIfPort->pfnDisconnectInterface(pTrunk->pIfPort, pIf->pvIfData);
4035
4036 /* Wait for the interface to quiesce while we still can. */
4037 intnetR0BusyWait(pNetwork, &pIf->cBusy);
4038
4039 /* Release our reference to the network. */
4040 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4041 pIf->pNetwork = NULL;
4042 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4043
4044 SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
4045 }
4046
4047 RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
4048
4049 /*
4050 * Wakeup anyone waiting on this interface.
4051 *
4052 * We *must* make sure they have woken up properly and realized
4053 * that the interface is no longer valid.
4054 */
4055 if (pIf->hRecvEvent != NIL_RTSEMEVENT)
4056 {
4057 RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
4058 unsigned cMaxWait = 0x1000;
4059 while (pIf->cSleepers && cMaxWait-- > 0)
4060 {
4061 RTSemEventSignal(hRecvEvent);
4062 RTThreadYield();
4063 }
4064 if (pIf->cSleepers)
4065 {
4066 RTThreadSleep(1);
4067
4068 cMaxWait = pIf->cSleepers;
4069 while (pIf->cSleepers && cMaxWait-- > 0)
4070 {
4071 RTSemEventSignal(hRecvEvent);
4072 RTThreadSleep(10);
4073 }
4074 }
4075
4076 RTSemEventDestroy(hRecvEvent);
4077 pIf->hRecvEvent = NIL_RTSEMEVENT;
4078 }
4079
4080 /*
4081 * Unmap user buffer.
4082 */
4083 if (pIf->pIntBuf != pIf->pIntBufDefault)
4084 {
4085 /** @todo user buffer */
4086 }
4087
4088 /*
4089 * Unmap and Free the default buffer.
4090 */
4091 if (pIf->pIntBufDefault)
4092 {
4093 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
4094 pIf->pIntBufDefault = NULL;
4095 pIf->pIntBufDefaultR3 = 0;
4096 pIf->pIntBuf = NULL;
4097 pIf->pIntBufR3 = 0;
4098 }
4099
4100 /*
4101 * Free remaining resources
4102 */
4103 RTSpinlockDestroy(pIf->hRecvInSpinlock);
4104 pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
4105
4106 RTMemFree(pIf->pDstTab);
4107 pIf->pDstTab = NULL;
4108
4109 for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
4110 intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
4111
4112 pIf->pvObj = NULL;
4113 RTMemFree(pIf);
4114}
4115
4116
4117/**
4118 * Creates a new network interface.
4119 *
4120 * The call must have opened the network for the new interface and is
4121 * responsible for closing it on failure. On success it must leave the network
4122 * opened so the interface destructor can close it.
4123 *
4124 * @returns VBox status code.
4125 * @param pNetwork The network, referenced. The reference is consumed on
4126 * success.
4127 * @param pSession The session handle.
4128 * @param cbSend The size of the send buffer.
4129 * @param cbRecv The size of the receive buffer.
4130 * @param phIf Where to store the interface handle.
4131 */
4132static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv,
4133 PINTNETIFHANDLE phIf)
4134{
4135 LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
4136 pNetwork, pSession, cbSend, cbRecv, phIf));
4137
4138 /*
4139 * Assert input.
4140 */
4141 AssertPtr(pNetwork);
4142 AssertPtr(phIf);
4143
4144 /*
4145 * Make sure that all destination tables as well as the have space of
4146 */
4147 int rc = intnetR0NetworkEnsureTabSpace(pNetwork);
4148 if (RT_FAILURE(rc))
4149 return rc;
4150
4151 /*
4152 * Allocate the interface and initalize it.
4153 */
4154 PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
4155 if (!pIf)
4156 return VERR_NO_MEMORY;
4157
4158 memset(&pIf->MacAddr, 0xff, sizeof(pIf->MacAddr)); /* broadcast */
4159 //pIf->fMacSet = false;
4160 //pIf->fPromiscuous = false;
4161 //pIf->fActive = false;
4162 //pIf->fDestroying = false;
4163 //pIf->cYields = 0;
4164 //pIf->pIntBuf = 0;
4165 //pIf->pIntBufR3 = NIL_RTR3PTR;
4166 //pIf->pIntBufDefault = 0;
4167 //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
4168 pIf->hRecvEvent = NIL_RTSEMEVENT;
4169 //pIf->cSleepers = 0;
4170 pIf->hIf = INTNET_HANDLE_INVALID;
4171 pIf->pNetwork = pNetwork;
4172 pIf->pSession = pSession;
4173 //pIf->pvObj = NULL;
4174 //pIf->aAddrCache = {0};
4175 pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
4176 pIf->cBusy = 0;
4177 //pIf->pDstTab = NULL;
4178 //pIf->pvIfData = NULL;
4179
4180 for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End && RT_SUCCESS(rc); i++)
4181 rc = intnetR0IfAddrCacheInit(&pIf->aAddrCache[i], (INTNETADDRTYPE)i,
4182 !!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
4183 if (RT_SUCCESS(rc))
4184 rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, (PINTNETDSTTAB *)&pIf->pDstTab);
4185 if (RT_SUCCESS(rc))
4186 rc = RTSemEventCreate((PRTSEMEVENT)&pIf->hRecvEvent);
4187 if (RT_SUCCESS(rc))
4188 rc = RTSpinlockCreate(&pIf->hRecvInSpinlock);
4189 if (RT_SUCCESS(rc))
4190 {
4191 /*
4192 * Create the default buffer.
4193 */
4194 /** @todo adjust with minimums and apply defaults here. */
4195 cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), INTNETRINGBUF_ALIGNMENT);
4196 cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), INTNETRINGBUF_ALIGNMENT);
4197 const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), INTNETRINGBUF_ALIGNMENT) + cbRecv + cbSend;
4198 rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
4199 if (RT_SUCCESS(rc))
4200 {
4201 ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
4202
4203 pIf->pIntBuf = pIf->pIntBufDefault;
4204 pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
4205 IntNetBufInit(pIf->pIntBuf, cbBuf, cbRecv, cbSend);
4206
4207 /*
4208 * Register the interface with the session and create a handle for it.
4209 */
4210 pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE,
4211 intnetR0IfDestruct, pIf, pNetwork->pIntNet);
4212 if (pIf->pvObj)
4213 {
4214 rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
4215 if (RT_SUCCESS(rc))
4216 {
4217 /*
4218 * Finally add the interface to the network, consuming the
4219 * network reference of the caller.
4220 */
4221 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4222 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4223
4224 uint32_t iIf = pNetwork->MacTab.cEntries;
4225 Assert(iIf + 1 <= pNetwork->MacTab.cEntriesAllocated);
4226
4227 pNetwork->MacTab.paEntries[iIf].MacAddr = pIf->MacAddr;
4228 pNetwork->MacTab.paEntries[iIf].fActive = false;
4229 pNetwork->MacTab.paEntries[iIf].fPromiscuous = false;
4230 pNetwork->MacTab.paEntries[iIf].pIf = pIf;
4231
4232 pNetwork->MacTab.cEntries = iIf + 1;
4233 pIf->pNetwork = pNetwork;
4234
4235 /*
4236 * Grab a busy reference (paranoia) to the trunk before releaseing
4237 * the spinlock and then notify it about the new interface.
4238 */
4239 PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
4240 if (pTrunk)
4241 intnetR0BusyIncTrunk(pTrunk);
4242
4243 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4244
4245 if (pTrunk)
4246 {
4247 Log(("intnetR0NetworkCreateIf: pfnConnectInterface hIf=%RX32\n", pIf->hIf));
4248 if (pTrunk->pIfPort)
4249 rc = pTrunk->pIfPort->pfnConnectInterface(pTrunk->pIfPort, pIf, &pIf->pvIfData);
4250 intnetR0BusyDecTrunk(pTrunk);
4251 }
4252 if (RT_SUCCESS(rc))
4253 {
4254 /*
4255 * We're good!
4256 */
4257 *phIf = pIf->hIf;
4258 Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
4259 *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
4260 return VINF_SUCCESS;
4261 }
4262 }
4263
4264 SUPR0ObjRelease(pIf->pvObj, pSession);
4265 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
4266 return rc;
4267 }
4268
4269 /* clean up */
4270 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
4271 pIf->pIntBufDefault = NULL;
4272 pIf->pIntBuf = NULL;
4273 }
4274 }
4275
4276 RTSpinlockDestroy(pIf->hRecvInSpinlock);
4277 pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
4278 RTSemEventDestroy(pIf->hRecvEvent);
4279 pIf->hRecvEvent = NIL_RTSEMEVENT;
4280 RTMemFree(pIf->pDstTab);
4281 for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
4282 intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
4283 RTMemFree(pIf);
4284 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
4285 return rc;
4286}
4287
4288
4289/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
4290static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
4291{
4292 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4293 AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
4294 return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
4295}
4296
4297
4298/** @copydoc INTNETTRUNKSWPORT::pfnReportMacAddress */
4299static DECLCALLBACK(void) intnetR0TrunkIfPortReportMacAddress(PINTNETTRUNKSWPORT pSwitchPort, PCRTMAC pMacAddr)
4300{
4301 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4302
4303 /*
4304 * Get the network instance and grab the address spinlock before making
4305 * any changes.
4306 */
4307 intnetR0BusyIncTrunk(pThis);
4308 PINTNETNETWORK pNetwork = pThis->pNetwork;
4309 if (pNetwork)
4310 {
4311 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4312 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4313
4314 pNetwork->MacTab.HostMac = *pMacAddr;
4315 pThis->MacAddr = *pMacAddr;
4316
4317 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4318 }
4319 else
4320 pThis->MacAddr = *pMacAddr;
4321 intnetR0BusyDecTrunk(pThis);
4322}
4323
4324
4325/** @copydoc INTNETTRUNKSWPORT::pfnReportPromiscuousMode */
4326static DECLCALLBACK(void) intnetR0TrunkIfPortReportPromiscuousMode(PINTNETTRUNKSWPORT pSwitchPort, bool fPromiscuous)
4327{
4328 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4329
4330 /*
4331 * Get the network instance and grab the address spinlock before making
4332 * any changes.
4333 */
4334 intnetR0BusyIncTrunk(pThis);
4335 PINTNETNETWORK pNetwork = pThis->pNetwork;
4336 if (pNetwork)
4337 {
4338 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4339 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4340
4341 pNetwork->MacTab.fHostPromiscuous = fPromiscuous;
4342
4343 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4344 }
4345 intnetR0BusyDecTrunk(pThis);
4346}
4347
4348
4349/** @copydoc INTNETTRUNKSWPORT::pfnReportGsoCapabilities */
4350static DECLCALLBACK(void) intnetR0TrunkIfPortReportGsoCapabilities(PINTNETTRUNKSWPORT pSwitchPort,
4351 uint32_t fGsoCapabilities, uint32_t fDst)
4352{
4353 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4354
4355 for (unsigned iBit = PDMNETWORKGSOTYPE_END; iBit < 32; iBit++)
4356 Assert(!(fGsoCapabilities & RT_BIT_32(iBit)));
4357 Assert(!(fDst & ~INTNETTRUNKDIR_VALID_MASK));
4358 Assert(fDst);
4359
4360 if (fDst & INTNETTRUNKDIR_HOST)
4361 pThis->fHostGsoCapabilites = fGsoCapabilities;
4362
4363 if (fDst & INTNETTRUNKDIR_WIRE)
4364 pThis->fWireGsoCapabilites = fGsoCapabilities;
4365}
4366
4367
4368/** @copydoc INTNETTRUNKSWPORT::pfnReportNoPreemptDsts */
4369static DECLCALLBACK(void) intnetR0TrunkIfPortReportNoPreemptDsts(PINTNETTRUNKSWPORT pSwitchPort, uint32_t fNoPreemptDsts)
4370{
4371 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4372 Assert(!(fNoPreemptDsts & ~INTNETTRUNKDIR_VALID_MASK));
4373
4374 pThis->fNoPreemptDsts = fNoPreemptDsts;
4375}
4376
4377
4378/** @copydoc INTNETTRUNKSWPORT::pfnPreRecv */
4379static DECLCALLBACK(INTNETSWDECISION) intnetR0TrunkIfPortPreRecv(PINTNETTRUNKSWPORT pSwitchPort,
4380 void const *pvSrc, size_t cbSrc, uint32_t fSrc)
4381{
4382 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4383 PINTNETNETWORK pNetwork = pThis->pNetwork;
4384
4385 /* assert some sanity */
4386 AssertPtrReturn(pNetwork, INTNETSWDECISION_TRUNK);
4387 AssertReturn(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT, INTNETSWDECISION_TRUNK);
4388 AssertPtr(pvSrc);
4389 AssertPtr(cbSrc >= 6);
4390 Assert(fSrc);
4391
4392 /** @todo implement the switch table. */
4393
4394 return INTNETSWDECISION_BROADCAST;
4395}
4396
4397
4398/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
4399static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, void *pvIf, PINTNETSG pSG, uint32_t fSrc)
4400{
4401 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4402
4403 /* assert some sanity */
4404 AssertPtr(pSG);
4405 Assert(fSrc);
4406 NOREF(pvIf); /* later */
4407
4408 /*
4409 * Mark the trunk as busy, make sure we've got a network and that there are
4410 * some active interfaces around.
4411 */
4412 bool fRc = false /* don't drop it */;
4413 intnetR0BusyIncTrunk(pThis);
4414 PINTNETNETWORK pNetwork = pThis->pNetwork;
4415 if (RT_LIKELY( pNetwork
4416 && pNetwork->cActiveIFs > 0 ))
4417 {
4418 /*
4419 * Grab or allocate a destination table.
4420 */
4421 bool const fIntCtx = RTThreadPreemptIsEnabled(NIL_RTTHREAD) || RTThreadIsInInterrupt(NIL_RTTHREAD);
4422 unsigned iDstTab = 0;
4423 PINTNETDSTTAB pDstTab = NULL;
4424 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4425 RTSpinlockAcquireNoInts(pThis->hDstTabSpinlock, &Tmp);
4426 if (fIntCtx)
4427 {
4428 /* Interrupt or restricted context. */
4429 iDstTab = RTMpCpuIdToSetIndex(RTMpCpuId());
4430 iDstTab %= pThis->cIntDstTabs;
4431 pDstTab = pThis->apIntDstTabs[iDstTab];
4432 if (RT_LIKELY(pDstTab))
4433 pThis->apIntDstTabs[iDstTab] = NULL;
4434 else
4435 {
4436 iDstTab = pThis->cIntDstTabs;
4437 while (iDstTab-- > 0)
4438 {
4439 pDstTab = pThis->apIntDstTabs[iDstTab];
4440 if (pDstTab)
4441 {
4442 pThis->apIntDstTabs[iDstTab] = NULL;
4443 break;
4444 }
4445 }
4446 }
4447 RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
4448 Assert(!pDstTab || iDstTab < pThis->cIntDstTabs);
4449 }
4450 else
4451 {
4452 /* Task context, fallback is to allocate a table. */
4453 AssertCompile(RT_ELEMENTS(pThis->apTaskDstTabs) == 2); /* for loop rollout */
4454 pDstTab = pThis->apIntDstTabs[iDstTab = 0];
4455 if (!pDstTab)
4456 pDstTab = pThis->apIntDstTabs[iDstTab = 1];
4457 if (pDstTab)
4458 {
4459 pThis->apIntDstTabs[iDstTab] = NULL;
4460 RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
4461 Assert(iDstTab < RT_ELEMENTS(pThis->apTaskDstTabs));
4462 }
4463 else
4464 {
4465 RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
4466 intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pDstTab);
4467 iDstTab = 65535;
4468 }
4469 }
4470 if (RT_LIKELY(pDstTab))
4471 {
4472 /*
4473 * Finally, get down to business of sending the frame.
4474 */
4475 INTNETSWDECISION enmSwDecision = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, pDstTab);
4476 AssertMsg(enmSwDecision != INTNETSWDECISION_BAD_CONTEXT, ("fSrc=%#x fTrunkDst=%#x hdr=%.14Rhxs\n", fSrc, pDstTab->fTrunkDst, pSG->aSegs[0].pv));
4477 if (enmSwDecision == INTNETSWDECISION_INTNET)
4478 fRc = true; /* drop it */
4479
4480 /*
4481 * Free the destination table.
4482 */
4483 if (iDstTab == 65535)
4484 RTMemFree(pDstTab);
4485 else
4486 {
4487 RTSpinlockAcquireNoInts(pThis->hDstTabSpinlock, &Tmp);
4488 if (fIntCtx && !pThis->apIntDstTabs[iDstTab])
4489 pThis->apIntDstTabs[iDstTab] = pDstTab;
4490 else if (!fIntCtx && !pThis->apTaskDstTabs[iDstTab])
4491 pThis->apTaskDstTabs[iDstTab] = pDstTab;
4492 else
4493 {
4494 /* this shouldn't happen! */
4495 PINTNETDSTTAB *papDstTabs = fIntCtx ? &pThis->apIntDstTabs[0] : &pThis->apTaskDstTabs[0];
4496 iDstTab = fIntCtx ? pThis->cIntDstTabs : RT_ELEMENTS(pThis->apTaskDstTabs);
4497 while (iDstTab-- > 0)
4498 if (!papDstTabs[iDstTab])
4499 {
4500 papDstTabs[iDstTab] = pDstTab;
4501 break;
4502 }
4503 }
4504 RTSpinlockReleaseNoInts(pThis->hDstTabSpinlock, &Tmp);
4505 Assert(iDstTab < RT_MAX(RT_ELEMENTS(pThis->apTaskDstTabs), pThis->cIntDstTabs));
4506 }
4507 }
4508 }
4509
4510 intnetR0BusyDecTrunk(pThis);
4511 return fRc;
4512}
4513
4514
4515/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
4516static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
4517{
4518 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4519 PINTNETNETWORK pNetwork = pThis->pNetwork;
4520
4521 /* assert some sanity */
4522 AssertPtrReturnVoid(pNetwork);
4523 AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
4524 AssertPtr(pSG);
4525 Assert(pSG->cUsers > 0 && pSG->cUsers < 256);
4526
4527 /* do it. */
4528 ++pSG->cUsers;
4529}
4530
4531
4532/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
4533static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
4534{
4535 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
4536 PINTNETNETWORK pNetwork = pThis->pNetwork;
4537
4538 /* assert some sanity */
4539 AssertPtrReturnVoid(pNetwork);
4540 AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
4541 AssertPtr(pSG);
4542 Assert(pSG->cUsers > 0);
4543
4544 /*
4545 * Free it?
4546 */
4547 if (!--pSG->cUsers)
4548 {
4549 /** @todo later */
4550 }
4551}
4552
4553
4554/**
4555 * Retain the trunk interface.
4556 *
4557 * @returns pThis if retained.
4558 *
4559 * @param pThis The trunk.
4560 *
4561 * @remarks Any locks.
4562 */
4563static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
4564{
4565 if (pThis && pThis->pIfPort)
4566 {
4567 pThis->pIfPort->pfnRetain(pThis->pIfPort);
4568 return pThis;
4569 }
4570 return NULL;
4571}
4572
4573
4574/**
4575 * Release the trunk interface.
4576 *
4577 * @param pThis The trunk.
4578 */
4579static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
4580{
4581 if (pThis && pThis->pIfPort)
4582 pThis->pIfPort->pfnRelease(pThis->pIfPort);
4583}
4584
4585
4586/**
4587 * Shutdown the trunk interface.
4588 *
4589 * @param pThis The trunk.
4590 * @param pNetworks The network.
4591 *
4592 * @remarks The caller must hold the global lock.
4593 */
4594static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
4595{
4596 /* assert sanity */
4597 if (!pThis)
4598 return;
4599 AssertPtr(pThis);
4600 Assert(pThis->pNetwork == pNetwork);
4601 AssertPtrNull(pThis->pIfPort);
4602
4603 /*
4604 * The interface has already been deactivated, we just to wait for
4605 * it to become idle before we can disconnect and release it.
4606 */
4607 PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
4608 if (pIfPort)
4609 {
4610 /* unset it */
4611 pThis->pIfPort = NULL;
4612
4613 /* wait in portions so we can complain ever now an then. */
4614 uint64_t StartTS = RTTimeSystemNanoTS();
4615 int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
4616 if (RT_FAILURE(rc))
4617 {
4618 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
4619 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
4620 Assert(rc == VERR_TIMEOUT);
4621 while ( RT_FAILURE(rc)
4622 && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
4623 rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
4624 if (rc == VERR_TIMEOUT)
4625 {
4626 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
4627 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
4628 while ( rc == VERR_TIMEOUT
4629 && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
4630 rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
4631 if (RT_FAILURE(rc))
4632 {
4633 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
4634 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
4635 AssertRC(rc);
4636 }
4637 }
4638 }
4639
4640 /* disconnect & release it. */
4641 pIfPort->pfnDisconnectAndRelease(pIfPort);
4642 }
4643
4644 /*
4645 * Free up the resources.
4646 */
4647 pThis->pNetwork = NULL;
4648 RTSpinlockDestroy(pThis->hDstTabSpinlock);
4649 for (unsigned i = 0; i < RT_ELEMENTS(pThis->apTaskDstTabs); i++)
4650 {
4651 Assert(pThis->apTaskDstTabs[i]);
4652 RTMemFree(pThis->apTaskDstTabs[i]);
4653 pThis->apTaskDstTabs[i] = NULL;
4654 }
4655 for (unsigned i = 0; i < pThis->cIntDstTabs; i++)
4656 {
4657 Assert(pThis->apIntDstTabs[i]);
4658 RTMemFree(pThis->apIntDstTabs[i]);
4659 pThis->apIntDstTabs[i] = NULL;
4660 }
4661 RTMemFree(pThis);
4662}
4663
4664
4665/**
4666 * Creates the trunk connection (if any).
4667 *
4668 * @returns VBox status code.
4669 *
4670 * @param pNetwork The newly created network.
4671 * @param pSession The session handle.
4672 */
4673static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
4674{
4675 const char *pszName;
4676 switch (pNetwork->enmTrunkType)
4677 {
4678 /*
4679 * The 'None' case, simple.
4680 */
4681 case kIntNetTrunkType_None:
4682 case kIntNetTrunkType_WhateverNone:
4683 return VINF_SUCCESS;
4684
4685 /* Can't happen, but makes GCC happy. */
4686 default:
4687 return VERR_NOT_IMPLEMENTED;
4688
4689 /*
4690 * Translate enum to component factory name.
4691 */
4692 case kIntNetTrunkType_NetFlt:
4693 pszName = "VBoxNetFlt";
4694 break;
4695 case kIntNetTrunkType_NetAdp:
4696#if defined(RT_OS_DARWIN) && !defined(VBOXNETADP_DO_NOT_USE_NETFLT)
4697 pszName = "VBoxNetFlt";
4698#else /* VBOXNETADP_DO_NOT_USE_NETFLT */
4699 pszName = "VBoxNetAdp";
4700#endif /* VBOXNETADP_DO_NOT_USE_NETFLT */
4701 break;
4702 case kIntNetTrunkType_SrvNat:
4703 pszName = "VBoxSrvNat";
4704 break;
4705 }
4706
4707 /*
4708 * Allocate the trunk interface and associated destination tables.
4709 *
4710 * We take a very optimistic view on the parallelism of the host
4711 * network stack and NIC driver. So, we allocate one table for each
4712 * possible CPU to deal with interrupt time requests and one for task
4713 * time calls.
4714 */
4715 RTCPUID cCpus = RTMpGetCount(); Assert(cCpus > 0);
4716 PINTNETTRUNKIF pTrunk = (PINTNETTRUNKIF)RTMemAllocZ(RT_OFFSETOF(INTNETTRUNKIF, apIntDstTabs[cCpus]));
4717 if (!pTrunk)
4718 return VERR_NO_MEMORY;
4719
4720 Assert(pNetwork->MacTab.cEntriesAllocated > 0);
4721 int rc = VINF_SUCCESS;
4722 pTrunk->cIntDstTabs = cCpus;
4723 for (unsigned i = 0; i < cCpus && RT_SUCCESS(rc); i++)
4724 rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apIntDstTabs[i]);
4725 for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs) && RT_SUCCESS(rc); i++)
4726 rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apTaskDstTabs[i]);
4727
4728 if (RT_SUCCESS(rc))
4729 {
4730 pTrunk->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
4731 pTrunk->SwitchPort.pfnPreRecv = intnetR0TrunkIfPortPreRecv;
4732 pTrunk->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
4733 pTrunk->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
4734 pTrunk->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
4735 pTrunk->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
4736 pTrunk->SwitchPort.pfnReportMacAddress = intnetR0TrunkIfPortReportMacAddress;
4737 pTrunk->SwitchPort.pfnReportPromiscuousMode = intnetR0TrunkIfPortReportPromiscuousMode;
4738 pTrunk->SwitchPort.pfnReportGsoCapabilities = intnetR0TrunkIfPortReportGsoCapabilities;
4739 pTrunk->SwitchPort.pfnReportNoPreemptDsts = intnetR0TrunkIfPortReportNoPreemptDsts;
4740 pTrunk->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
4741 //pTrunk->pIfPort = NULL;
4742 pTrunk->pNetwork = pNetwork;
4743 pTrunk->MacAddr.au8[0] = 0xff;
4744 pTrunk->MacAddr.au8[1] = 0xff;
4745 pTrunk->MacAddr.au8[2] = 0xff;
4746 pTrunk->MacAddr.au8[3] = 0xff;
4747 pTrunk->MacAddr.au8[4] = 0xff;
4748 pTrunk->MacAddr.au8[5] = 0xff;
4749 //pTrunk->fPhysSG = false;
4750 //pTrunk->fUnused = false;
4751 //pTrunk->cBusy = 0;
4752 //pTrunk->fNoPreemptDsts = 0;
4753 //pTrunk->fWireGsoCapabilites = 0;
4754 //pTrunk->fHostGsoCapabilites = 0;
4755 //pTrunk->abGsoHdrs = {0};
4756 pTrunk->hDstTabSpinlock = NIL_RTSPINLOCK;
4757 //pTrunk->apTaskDstTabs = above;
4758 //pTrunk->cIntDstTabs = above;
4759 //pTrunk->apIntDstTabs = above;
4760
4761 /*
4762 * Create the lock (we've NIL'ed the members above to simplify cleanup).
4763 */
4764 rc = RTSpinlockCreate(&pTrunk->hDstTabSpinlock);
4765 if (RT_SUCCESS(rc))
4766 {
4767 /*
4768 * There are a couple of bits in MacTab as well pertaining to the
4769 * trunk. We have to set this before it's reported.
4770 *
4771 * Note! We don't need to lock the MacTab here - creation time.
4772 */
4773 pNetwork->MacTab.pTrunk = pTrunk;
4774 pNetwork->MacTab.HostMac = pTrunk->MacAddr;
4775 pNetwork->MacTab.fHostPromiscuous = false;
4776 pNetwork->MacTab.fHostActive = false;
4777 pNetwork->MacTab.fWirePromiscuous = false; /** @todo !!(fFlags & INTNET_OPEN_FLAGS_PROMISC_TRUNK_WIRE); */
4778 pNetwork->MacTab.fWireActive = false;
4779
4780#ifdef IN_RING0 /* (testcase is ring-3) */
4781 /*
4782 * Query the factory we want, then use it create and connect the trunk.
4783 */
4784 PINTNETTRUNKFACTORY pTrunkFactory = NULL;
4785 rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
4786 if (RT_SUCCESS(rc))
4787 {
4788 rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory,
4789 pNetwork->szTrunk,
4790 &pTrunk->SwitchPort,
4791 pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE
4792 ? INTNETTRUNKFACTORY_FLAG_NO_PROMISC
4793 : 0,
4794 &pTrunk->pIfPort);
4795 pTrunkFactory->pfnRelease(pTrunkFactory);
4796 if (RT_SUCCESS(rc))
4797 {
4798 Assert(pTrunk->pIfPort);
4799
4800 Log(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
4801 pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
4802 return VINF_SUCCESS;
4803 }
4804 }
4805#else /* IN_RING3 */
4806 rc = VERR_NOT_SUPPORTED;
4807#endif /* IN_RING3 */
4808
4809 pNetwork->MacTab.pTrunk = NULL;
4810 }
4811
4812 /* bail out and clean up. */
4813 RTSpinlockDestroy(pTrunk->hDstTabSpinlock);
4814 }
4815
4816 for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs); i++)
4817 RTMemFree(pTrunk->apTaskDstTabs[i]);
4818 for (unsigned i = 0; i < pTrunk->cIntDstTabs; i++)
4819 RTMemFree(pTrunk->apIntDstTabs[i]);
4820 RTMemFree(pTrunk);
4821
4822 LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
4823 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
4824 return rc;
4825}
4826
4827
4828
4829/**
4830 * Object destructor callback.
4831 * This is called for reference counted objectes when the count reaches 0.
4832 *
4833 * @param pvObj The object pointer.
4834 * @param pvUser1 Pointer to the network.
4835 * @param pvUser2 Pointer to the INTNET instance data.
4836 */
4837static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
4838{
4839 PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
4840 PINTNET pIntNet = (PINTNET)pvUser2;
4841 Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
4842 Assert(pNetwork->pIntNet == pIntNet);
4843
4844 /* Take the big create/open/destroy sem. */
4845 RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
4846
4847 /*
4848 * Tell the trunk, if present, that we're about to disconnect it and wish
4849 * no further calls from it.
4850 */
4851 PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
4852 if (pTrunk)
4853 pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, INTNETTRUNKIFSTATE_DISCONNECTING);
4854
4855 /*
4856 * Deactivate and orphan any remaining interfaces and wait for them to idle.
4857 *
4858 * Note! Normally there are no more interfaces at this point, however, when
4859 * supdrvCloseSession / supdrvCleanupSession release the objects the
4860 * order is undefined. So, it's quite possible that the network will
4861 * be dereference and destroyed before the interfaces.
4862 */
4863 RTSPINLOCKTMP Tmp = RTSPINLOCKTMP_INITIALIZER;
4864 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4865
4866 uint32_t iIf = pNetwork->MacTab.cEntries;
4867 while (iIf-- > 0)
4868 {
4869 pNetwork->MacTab.paEntries[iIf].fActive = false;
4870 pNetwork->MacTab.paEntries[iIf].pIf->fActive = false;
4871 }
4872
4873 pNetwork->MacTab.fHostActive = false;
4874 pNetwork->MacTab.fWireActive = false;
4875
4876 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4877
4878 /* Wait for all the interfaces to quiesce. (Interfaces cannot be
4879 removed / added since we're holding the big lock.) */
4880 if (pTrunk)
4881 intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
4882
4883 iIf = pNetwork->MacTab.cEntries;
4884 while (iIf-- > 0)
4885 intnetR0BusyWait(pNetwork, &pNetwork->MacTab.paEntries[iIf].pIf->cBusy);
4886
4887 /* Orphan the interfaces (not trunk). Don't bother with calling
4888 pfnDisconnectInterface here since the networking is going away. */
4889 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4890 while ((iIf = pNetwork->MacTab.cEntries) > 0)
4891 {
4892 PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf - 1].pIf;
4893 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4894
4895 intnetR0BusyWait(pNetwork, &pIf->cBusy);
4896
4897 RTSpinlockAcquireNoInts(pNetwork->hAddrSpinlock, &Tmp);
4898 if ( iIf == pNetwork->MacTab.cEntries /* paranoia */
4899 && pIf->cBusy)
4900 {
4901 pIf->pNetwork = NULL;
4902 pNetwork->MacTab.cEntries--;
4903 }
4904 }
4905
4906 /*
4907 * Zap the trunk pointer while we still own the spinlock, destroy the
4908 * trunk after we've left it. Note that this might take a while...
4909 */
4910 pNetwork->MacTab.pTrunk = NULL;
4911
4912 RTSpinlockReleaseNoInts(pNetwork->hAddrSpinlock, &Tmp);
4913
4914 if (pTrunk)
4915 intnetR0TrunkIfDestroy(pTrunk, pNetwork);
4916
4917 /*
4918 * Unlink the network.
4919 * Note that it needn't be in the list if we failed during creation.
4920 */
4921 PINTNETNETWORK pPrev = pIntNet->pNetworks;
4922 if (pPrev == pNetwork)
4923 pIntNet->pNetworks = pNetwork->pNext;
4924 else
4925 {
4926 for (; pPrev; pPrev = pPrev->pNext)
4927 if (pPrev->pNext == pNetwork)
4928 {
4929 pPrev->pNext = pNetwork->pNext;
4930 break;
4931 }
4932 }
4933 pNetwork->pNext = NULL;
4934 pNetwork->pvObj = NULL;
4935
4936 /*
4937 * Free resources.
4938 */
4939 RTSemEventDestroy(pNetwork->hEvtBusyIf);
4940 pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
4941 RTSpinlockDestroy(pNetwork->hAddrSpinlock);
4942 pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
4943 RTMemFree(pNetwork->MacTab.paEntries);
4944 pNetwork->MacTab.paEntries = NULL;
4945 RTMemFree(pNetwork);
4946
4947 /* Release the create/destroy sem. */
4948 RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
4949}
4950
4951
4952/**
4953 * Opens an existing network.
4954 *
4955 * The call must own the INTNET::hMtxCreateOpenDestroy.
4956 *
4957 * @returns VBox status code.
4958 * @param pIntNet The instance data.
4959 * @param pSession The current session.
4960 * @param pszNetwork The network name. This has a valid length.
4961 * @param enmTrunkType The trunk type.
4962 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
4963 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
4964 * @param ppNetwork Where to store the pointer to the network on success.
4965 */
4966static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
4967 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
4968{
4969 LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
4970 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
4971
4972 /* just pro forma validation, the caller is internal. */
4973 AssertPtr(pIntNet);
4974 AssertPtr(pSession);
4975 AssertPtr(pszNetwork);
4976 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
4977 AssertPtr(pszTrunk);
4978 Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
4979 AssertPtr(ppNetwork);
4980 *ppNetwork = NULL;
4981
4982 /*
4983 * Search networks by name.
4984 */
4985 PINTNETNETWORK pCur;
4986 uint8_t cchName = (uint8_t)strlen(pszNetwork);
4987 Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
4988
4989 pCur = pIntNet->pNetworks;
4990 while (pCur)
4991 {
4992 if ( pCur->cchName == cchName
4993 && !memcmp(pCur->szName, pszNetwork, cchName))
4994 {
4995 /*
4996 * Found the network, now check that we have the same ideas
4997 * about the trunk setup and security.
4998 */
4999 int rc;
5000 if ( enmTrunkType == kIntNetTrunkType_WhateverNone
5001 || ( pCur->enmTrunkType == enmTrunkType
5002 && !strcmp(pCur->szTrunk, pszTrunk)))
5003 {
5004 if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
5005 {
5006
5007 /*
5008 * Increment the reference and check that the session
5009 * can access this network.
5010 */
5011 rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
5012 if (RT_SUCCESS(rc))
5013 {
5014 if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
5015 rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
5016 if (RT_SUCCESS(rc))
5017 {
5018 pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
5019
5020 *ppNetwork = pCur;
5021 }
5022 else
5023 SUPR0ObjRelease(pCur->pvObj, pSession);
5024 }
5025 else if (rc == VERR_WRONG_ORDER)
5026 rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
5027 }
5028 else
5029 rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
5030 }
5031 else
5032 rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
5033
5034 LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
5035 return rc;
5036 }
5037
5038 pCur = pCur->pNext;
5039 }
5040
5041 LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
5042 return VERR_NOT_FOUND;
5043}
5044
5045
5046/**
5047 * Creates a new network.
5048 *
5049 * The call must own the INTNET::hMtxCreateOpenDestroy and has already attempted
5050 * opening the network and found it to be non-existing.
5051 *
5052 * @returns VBox status code.
5053 * @param pIntNet The instance data.
5054 * @param pSession The session handle.
5055 * @param pszNetwork The name of the network. This must be at least one character long and no longer
5056 * than the INTNETNETWORK::szName.
5057 * @param enmTrunkType The trunk type.
5058 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
5059 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
5060 * @param ppNetwork Where to store the network. In the case of failure
5061 * whatever is returned here should be dereferenced
5062 * outside the INTNET::hMtxCreateOpenDestroy.
5063 */
5064static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
5065 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
5066{
5067 LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
5068 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
5069
5070 /* just pro forma validation, the caller is internal. */
5071 AssertPtr(pIntNet);
5072 AssertPtr(pSession);
5073 AssertPtr(pszNetwork);
5074 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
5075 AssertPtr(pszTrunk);
5076 Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
5077 AssertPtr(ppNetwork);
5078 *ppNetwork = NULL;
5079
5080 /*
5081 * Allocate and initialize.
5082 */
5083 size_t cb = sizeof(INTNETNETWORK);
5084 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
5085 cb += INTNETNETWORK_TMP_SIZE + 64;
5086 PINTNETNETWORK pNetwork = (PINTNETNETWORK)RTMemAllocZ(cb);
5087 if (!pNetwork)
5088 return VERR_NO_MEMORY;
5089 //pNetwork->pNext = NULL;
5090 //pNetwork->pIfs = NULL;
5091 pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
5092 pNetwork->MacTab.cEntries = 0;
5093 pNetwork->MacTab.cEntriesAllocated = INTNET_GROW_DSTTAB_SIZE;
5094 pNetwork->MacTab.paEntries = NULL;
5095 pNetwork->MacTab.fHostPromiscuous = false;
5096 pNetwork->MacTab.fHostActive = false;
5097 pNetwork->MacTab.fWirePromiscuous = false;
5098 pNetwork->MacTab.fWireActive = false;
5099 pNetwork->MacTab.pTrunk = NULL;
5100 pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
5101 pNetwork->pIntNet = pIntNet;
5102 //pNetwork->pvObj = NULL;
5103 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
5104 pNetwork->pbTmp = RT_ALIGN_PT(pNetwork + 1, 64, uint8_t *);
5105 //else
5106 // pNetwork->pbTmp = NULL;
5107 pNetwork->fFlags = fFlags;
5108 //pNetwork->cActiveIFs = 0;
5109 size_t cchName = strlen(pszNetwork);
5110 pNetwork->cchName = (uint8_t)cchName;
5111 Assert(cchName && cchName < sizeof(pNetwork->szName)); /* caller's responsibility. */
5112 memcpy(pNetwork->szName, pszNetwork, cchName); /* '\0' at courtesy of alloc. */
5113 pNetwork->enmTrunkType = enmTrunkType;
5114 Assert(strlen(pszTrunk) < sizeof(pNetwork->szTrunk)); /* caller's responsibility. */
5115 strcpy(pNetwork->szTrunk, pszTrunk);
5116
5117 /*
5118 * Create the semaphore, spinlock and allocate the interface table.
5119 */
5120 int rc = RTSemEventCreate(&pNetwork->hEvtBusyIf);
5121 if (RT_SUCCESS(rc))
5122 rc = RTSpinlockCreate(&pNetwork->hAddrSpinlock);
5123 if (RT_SUCCESS(rc))
5124 {
5125 pNetwork->MacTab.paEntries = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * pNetwork->MacTab.cEntriesAllocated);
5126 if (!pNetwork->MacTab.paEntries)
5127 rc = VERR_NO_MEMORY;
5128 }
5129 if (RT_SUCCESS(rc))
5130 {
5131 /*
5132 * Register the object in the current session and link it into the network list.
5133 */
5134 pNetwork->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNetwork, pIntNet);
5135 if (pNetwork->pvObj)
5136 {
5137 pNetwork->pNext = pIntNet->pNetworks;
5138 pIntNet->pNetworks = pNetwork;
5139
5140 /*
5141 * Check if the current session is actually allowed to create and
5142 * open the network. It is possible to implement network name
5143 * based policies and these must be checked now. SUPR0ObjRegister
5144 * does no such checks.
5145 */
5146 rc = SUPR0ObjVerifyAccess(pNetwork->pvObj, pSession, pNetwork->szName);
5147 if (RT_SUCCESS(rc))
5148 {
5149 /*
5150 * Connect the trunk.
5151 */
5152 rc = intnetR0NetworkCreateTrunkIf(pNetwork, pSession);
5153 if (RT_SUCCESS(rc))
5154 {
5155 *ppNetwork = pNetwork;
5156 LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNetwork));
5157 return VINF_SUCCESS;
5158 }
5159 }
5160
5161 SUPR0ObjRelease(pNetwork->pvObj, pSession);
5162 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
5163 return rc;
5164 }
5165
5166 /* cleanup */
5167 rc = VERR_NO_MEMORY;
5168 }
5169
5170 RTSemEventDestroy(pNetwork->hEvtBusyIf);
5171 pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
5172 RTSpinlockDestroy(pNetwork->hAddrSpinlock);
5173 pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
5174 RTMemFree(pNetwork->MacTab.paEntries);
5175 pNetwork->MacTab.paEntries = NULL;
5176 RTMemFree(pNetwork);
5177
5178 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
5179 return rc;
5180}
5181
5182
5183/**
5184 * Opens a network interface and connects it to the specified network.
5185 *
5186 * @returns VBox status code.
5187 * @param pSession The session handle.
5188 * @param pszNetwork The network name.
5189 * @param enmTrunkType The trunk type.
5190 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
5191 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
5192 * @param fRestrictAccess Whether new participants should be subjected to access check or not.
5193 * @param cbSend The send buffer size.
5194 * @param cbRecv The receive buffer size.
5195 * @param phIf Where to store the handle to the network interface.
5196 */
5197INTNETR0DECL(int) IntNetR0Open(PSUPDRVSESSION pSession, const char *pszNetwork,
5198 INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
5199 uint32_t cbSend, uint32_t cbRecv, PINTNETIFHANDLE phIf)
5200{
5201 LogFlow(("IntNetR0Open: pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
5202 pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, cbSend, cbRecv, phIf));
5203
5204 /*
5205 * Validate input.
5206 */
5207 PINTNET pIntNet = g_pIntNet;
5208 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
5209 AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
5210
5211 AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
5212 const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
5213 AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
5214 size_t cchNetwork = pszNetworkEnd - pszNetwork;
5215 AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
5216
5217 if (pszTrunk)
5218 {
5219 AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
5220 const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
5221 AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
5222 }
5223 else
5224 pszTrunk = "";
5225
5226 AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
5227 ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
5228 switch (enmTrunkType)
5229 {
5230 case kIntNetTrunkType_None:
5231 case kIntNetTrunkType_WhateverNone:
5232 if (*pszTrunk)
5233 return VERR_INVALID_PARAMETER;
5234 break;
5235
5236 case kIntNetTrunkType_NetFlt:
5237 case kIntNetTrunkType_NetAdp:
5238 if (!*pszTrunk)
5239 return VERR_INVALID_PARAMETER;
5240 break;
5241
5242 default:
5243 return VERR_NOT_IMPLEMENTED;
5244 }
5245
5246 AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
5247 AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
5248
5249 /*
5250 * Acquire the mutex to serialize open/create/close.
5251 */
5252 int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
5253 if (RT_FAILURE(rc))
5254 return rc;
5255
5256 /*
5257 * Try open / create the network and create an interface on it for the
5258 * caller to use.
5259 */
5260 PINTNETNETWORK pNetwork = NULL;
5261 rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
5262 if (RT_SUCCESS(rc))
5263 {
5264 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, phIf);
5265 if (RT_SUCCESS(rc))
5266 rc = VINF_ALREADY_INITIALIZED;
5267 else
5268 SUPR0ObjRelease(pNetwork->pvObj, pSession);
5269 }
5270 else if (rc == VERR_NOT_FOUND)
5271 {
5272 rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
5273 if (RT_SUCCESS(rc))
5274 {
5275 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, phIf);
5276 if (RT_FAILURE(rc))
5277 SUPR0ObjRelease(pNetwork->pvObj, pSession);
5278 }
5279 }
5280
5281 RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
5282 LogFlow(("IntNetR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
5283 return rc;
5284}
5285
5286
5287/**
5288 * VMMR0 request wrapper for IntNetR0Open.
5289 *
5290 * @returns see GMMR0MapUnmapChunk.
5291 * @param pSession The caller's session.
5292 * @param pReq The request packet.
5293 */
5294INTNETR0DECL(int) IntNetR0OpenReq(PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
5295{
5296 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
5297 return VERR_INVALID_PARAMETER;
5298 return IntNetR0Open(pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
5299 pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
5300}
5301
5302
5303/**
5304 * Count the internal networks.
5305 *
5306 * This is mainly for providing the testcase with some introspection to validate
5307 * behavior when closing interfaces.
5308 *
5309 * @returns The number of networks.
5310 */
5311INTNETR0DECL(uint32_t) IntNetR0GetNetworkCount(void)
5312{
5313 /*
5314 * Grab the instance.
5315 */
5316 PINTNET pIntNet = g_pIntNet;
5317 if (!pIntNet)
5318 return 0;
5319 AssertPtrReturn(pIntNet, 0);
5320 AssertReturn(pIntNet->u32Magic == INTNET_MAGIC, 0);
5321
5322 /*
5323 * Grab the mutex and count the networks.
5324 */
5325 int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
5326 if (RT_FAILURE(rc))
5327 return 0;
5328
5329 uint32_t cNetworks = 0;
5330 for (PINTNETNETWORK pCur = pIntNet->pNetworks; pCur; pCur = pCur->pNext)
5331 cNetworks++;
5332
5333 RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
5334
5335 return cNetworks;
5336}
5337
5338
5339
5340/**
5341 * Destroys an instance of the Ring-0 internal networking service.
5342 */
5343INTNETR0DECL(void) IntNetR0Term(void)
5344{
5345 LogFlow(("IntNetR0Term:\n"));
5346
5347 /*
5348 * Zap the global pointer and validate it.
5349 */
5350 PINTNET pIntNet = g_pIntNet;
5351 g_pIntNet = NULL;
5352 if (!pIntNet)
5353 return;
5354 AssertPtrReturnVoid(pIntNet);
5355 AssertReturnVoid(pIntNet->u32Magic == INTNET_MAGIC);
5356
5357 /*
5358 * There is not supposed to be any networks hanging around at this time.
5359 */
5360 AssertReturnVoid(ASMAtomicCmpXchgU32(&pIntNet->u32Magic, ~INTNET_MAGIC, INTNET_MAGIC));
5361 Assert(pIntNet->pNetworks == NULL);
5362 if (pIntNet->hMtxCreateOpenDestroy != NIL_RTSEMMUTEX)
5363 {
5364 RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
5365 pIntNet->hMtxCreateOpenDestroy = NIL_RTSEMMUTEX;
5366 }
5367 if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
5368 {
5369 /** @todo does it make sense to have a deleter here? */
5370 RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
5371 pIntNet->hHtIfs = NIL_RTHANDLETABLE;
5372 }
5373
5374 RTMemFree(pIntNet);
5375}
5376
5377
5378/**
5379 * Initalizes the internal network ring-0 service.
5380 *
5381 * @returns VBox status code.
5382 */
5383INTNETR0DECL(int) IntNetR0Init(void)
5384{
5385 LogFlow(("IntNetR0Init:\n"));
5386 int rc = VERR_NO_MEMORY;
5387 PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
5388 if (pIntNet)
5389 {
5390 //pIntNet->pNetworks = NULL;
5391
5392 rc = RTSemMutexCreate(&pIntNet->hMtxCreateOpenDestroy);
5393 if (RT_SUCCESS(rc))
5394 {
5395 rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
5396 UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
5397 if (RT_SUCCESS(rc))
5398 {
5399 pIntNet->u32Magic = INTNET_MAGIC;
5400 g_pIntNet = pIntNet;
5401 LogFlow(("IntNetR0Init: returns VINF_SUCCESS pIntNet=%p\n", pIntNet));
5402 return VINF_SUCCESS;
5403 }
5404
5405 RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
5406 }
5407 RTMemFree(pIntNet);
5408 }
5409 LogFlow(("IntNetR0Init: returns %Rrc\n", rc));
5410 return rc;
5411}
5412
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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