VirtualBox

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

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

SrvIntNetR0.cpp: Implemented pfnPreRecv.

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

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