VirtualBox

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

Last change on this file since 44582 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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

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