VirtualBox

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

Last change on this file since 96407 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use