VirtualBox

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

最後變更 在這個檔案從63015是 62956,由 vboxsync 提交於 9 年 前

@copydoc -> @interface_method_impl

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

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