VirtualBox

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

Last change on this file since 28831 was 28831, checked in by vboxsync, 14 years ago

IntNet,VBoxNetFlt: Cleaned up the locking protocol between IntNet and NetFlt. Eleminated the out-bound trunk lock that IntNet always took when calling NetFlt.

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

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