VirtualBox

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

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

DrvIntNet: todo

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

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