VirtualBox

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

Last change on this file since 28739 was 28723, checked in by vboxsync, 15 years ago

IntNet/NetFlt: Added INTNETTRUNKSWPORT::pfnReportGsoCapabilities and enabled ring-0 transmission for linux hosts and wires.

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