VirtualBox

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

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

IntNet: Update MAC dst addr before comparing it to host's addr (#5905).

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

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