VirtualBox

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

Last change on this file since 52283 was 52134, checked in by vboxsync, 10 years ago

NDIS6: Cumulative commit containing async send, unload race fix (intnet), missing offload fix, basic vboxnetadp6.sys (#7231)

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