VirtualBox

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

最後變更 在這個檔案從39041是 39041,由 vboxsync 提交於 13 年 前

SrvIntNetR0.cpp: Dead code.

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

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