VirtualBox

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

Last change on this file since 30045 was 30029, checked in by vboxsync, 14 years ago

#4814: vboxnetflt qdisc: Drop host->guest packets on linux.

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