VirtualBox

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

Last change on this file since 54934 was 54868, checked in by vboxsync, 10 years ago

SrvIntNetR0: fix ref. counting when pfnConnectInterface() fails.

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