VirtualBox

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

Last change on this file since 76882 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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