VirtualBox

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

Last change on this file since 61497 was 60819, checked in by vboxsync, 9 years ago

Network/SrvIntR0: Fix debug assertion

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

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