VirtualBox

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

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

build fixes

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