VirtualBox

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

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

SrvIntNetR0.cpp: Addressed the allow-network promiscuous mode policy issue wrt related trunk traffic.

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