VirtualBox

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

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

Automated rebranding to Oracle copyright/license strings via filemuncher

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