VirtualBox

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

Last change on this file since 11074 was 11074, checked in by vboxsync, 17 years ago

intnet: Disabled the DHCP snooping code by putting it inside #ifdef INTNET_WITH_DHCP_SNOOPING. It doesn't do anything yet, so no reason to waste time on it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 137.2 KB
Line 
1/* $Id: SrvIntNetR0.cpp 11074 2008-08-02 01:42:46Z vboxsync $ */
2/** @file
3 * Internal networking - The ring 0 service.
4 */
5
6/*
7 * Copyright (C) 2006-2008 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/sup.h>
29#include <VBox/pdm.h>
30#include <VBox/log.h>
31#include <iprt/asm.h>
32#include <iprt/alloc.h>
33#include <iprt/semaphore.h>
34#include <iprt/spinlock.h>
35#include <iprt/thread.h>
36#include <iprt/assert.h>
37#include <iprt/string.h>
38#include <iprt/time.h>
39#include <iprt/handletable.h>
40#include <iprt/net.h>
41
42
43/*******************************************************************************
44* Defined Constants And Macros *
45*******************************************************************************/
46/** @def INTNET_WITH_DHCP_SNOOPING
47 * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
48/*#define INTNET_WITH_DHCP_SNOOPING - the implementation isn't completed yet. */
49
50
51/*******************************************************************************
52* Structures and Typedefs *
53*******************************************************************************/
54typedef enum INTNETADDRTYPE
55{
56 /** The invalid 0 entry. */
57 kIntNetAddrType_Invalid = 0,
58 /** IP version 4. */
59 kIntNetAddrType_IPv4,
60 /** IP version 6. */
61 kIntNetAddrType_IPv6,
62 /** IPX. */
63 kIntNetAddrType_IPX,
64 /** The end of the valid values. */
65 kIntNetAddrType_End,
66 /** The usual 32-bit hack. */
67 kIntNetAddrType_32BitHack = 0x7fffffff
68} INTNETADDRTYPE;
69/** Pointer to a network layer address type. */
70typedef INTNETADDRTYPE *PINTNETADDRTYPE;
71
72
73/**
74 * Address and type.
75 */
76typedef struct INTNETADDR
77{
78 /** The address type. */
79 INTNETADDRTYPE enmType;
80 /** The address. */
81 RTNETADDRU Addr;
82} INTNETADDR;
83/** Pointer to an address. */
84typedef INTNETADDR *PINTNETADDR;
85/** Pointer to a const address. */
86typedef INTNETADDR const *PCINTNETADDR;
87
88
89/**
90 * Address cache for a specific network layer.
91 */
92typedef struct INTNETADDRCACHE
93{
94 /** Pointer to the table of addresses. */
95 uint8_t *pbEntries;
96 /** The number of valid address entries. */
97 uint8_t cEntries;
98 /** The number of allocated address entries. */
99 uint8_t cEntriesAlloc;
100 /** The address size. */
101 uint8_t cbAddress;
102 /** The size of an entry. */
103 uint8_t cbEntry;
104} INTNETADDRCACHE;
105/** Pointer to an address cache. */
106typedef INTNETADDRCACHE *PINTNETADDRCACHE;
107/** Pointer to a const address cache. */
108typedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
109
110
111/**
112 * A network interface.
113 *
114 * Unless explicitly stated, all members are protect by the network semaphore.
115 */
116typedef struct INTNETIF
117{
118 /** Pointer to the next interface.
119 * This is protected by the INTNET::FastMutex. */
120 struct INTNETIF *pNext;
121 /** The current MAC address for the interface. */
122 RTMAC Mac;
123 /** Set if the INTNET::Mac member is valid. */
124 bool fMacSet;
125 /** Set if the interface is in promiscuous mode.
126 * In promiscuous mode the interface will receive all packages except the one it's sending. */
127 bool fPromiscuous;
128 /** Whether the interface is active or not. */
129 bool fActive;
130 /** Whether someone is currently in the destructor. */
131 bool volatile fDestroying;
132 /** Number of yields done to try make the interface read pending data.
133 * We will stop yeilding when this reaches a threshold assuming that the VM is paused or
134 * that it simply isn't worth all the delay. It is cleared when a successful send has been done.
135 */
136 uint32_t cYields;
137 /** Pointer to the current exchange buffer (ring-0). */
138 PINTNETBUF pIntBuf;
139 /** Pointer to ring-3 mapping of the current exchange buffer. */
140 R3PTRTYPE(PINTNETBUF) pIntBufR3;
141 /** Pointer to the default exchange buffer for the interface. */
142 PINTNETBUF pIntBufDefault;
143 /** Pointer to ring-3 mapping of the default exchange buffer. */
144 R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
145 /** Event semaphore which a receiver thread will sleep on while waiting for data to arrive. */
146 RTSEMEVENT volatile Event;
147 /** Number of threads sleeping on the Event semaphore. */
148 uint32_t cSleepers;
149 /** The interface handle.
150 * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
151 * should return with the appropriate error condition. */
152 INTNETIFHANDLE volatile hIf;
153 /** Pointer to the network this interface is connected to.
154 * This is protected by the INTNET::FastMutex. */
155 struct INTNETNETWORK *pNetwork;
156 /** The session this interface is associated with. */
157 PSUPDRVSESSION pSession;
158 /** The SUPR0 object id. */
159 void *pvObj;
160 /** The network layer address cache. (Indexed by type, 0 entry isn't used.) */
161 INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
162} INTNETIF;
163/** Pointer to an internal network interface. */
164typedef INTNETIF *PINTNETIF;
165
166
167/**
168 * A trunk interface.
169 */
170typedef struct INTNETTRUNKIF
171{
172 /** The port interface we present to the component. */
173 INTNETTRUNKSWPORT SwitchPort;
174 /** The port interface we get from the component. */
175 PINTNETTRUNKIFPORT pIfPort;
176 /** The trunk mutex that serializes all calls <b>to</b> the component. */
177 RTSEMFASTMUTEX FastMutex;
178 /** Pointer to the network we're connect to.
179 * This may be NULL if we're orphaned? */
180 struct INTNETNETWORK *pNetwork;
181 /** The cached MAC address of the interface the trunk is attached to.
182 * This is for the situations where we cannot take the out-bound
183 * semaphore (the recv case) but need to make frame edits (ARP). */
184 RTMAC CachedMac;
185 /** Whether to supply physical addresses with the outbound SGs. */
186 bool volatile fPhysSG;
187 /** Set if the 'wire' is in promiscuous mode.
188 * The state of the 'host' is queried each time. */
189 bool fPromiscuousWire;
190} INTNETTRUNKIF;
191/** Pointer to a trunk interface. */
192typedef INTNETTRUNKIF *PINTNETTRUNKIF;
193
194/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
195#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
196
197
198/**
199 * Internal representation of a network.
200 */
201typedef struct INTNETNETWORK
202{
203 /** The Next network in the chain.
204 * This is protected by the INTNET::FastMutex. */
205 struct INTNETNETWORK *pNext;
206 /** List of interfaces connected to the network.
207 * This is protected by the INTNET::FastMutex. */
208 PINTNETIF pIFs;
209 /** Pointer to the trunk interface.
210 * Can be NULL if there is no trunk connection. */
211 PINTNETTRUNKIF pTrunkIF;
212 /** The network mutex.
213 * It protects everything dealing with this network. */
214 RTSEMFASTMUTEX FastMutex;
215 /** Pointer to the instance data. */
216 struct INTNET *pIntNet;
217 /** The SUPR0 object id. */
218 void *pvObj;
219 /** Pointer to the temporary buffer that is used when snooping fragmented packets.
220 * This is allocated after this structure if we're sharing the MAC address with
221 * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundrary. */
222 uint8_t *pbTmp;
223 /** Network creation flags (INTNET_OPEN_FLAGS_*). */
224 uint32_t fFlags;
225 /** The number of active interfaces (excluding the trunk). */
226 uint32_t cActiveIFs;
227 /** The length of the network name. */
228 uint8_t cchName;
229 /** The network name. */
230 char szName[INTNET_MAX_NETWORK_NAME];
231 /** The trunk type. */
232 INTNETTRUNKTYPE enmTrunkType;
233 /** The trunk name. */
234 char szTrunk[INTNET_MAX_TRUNK_NAME];
235} INTNETNETWORK;
236/** Pointer to an internal network. */
237typedef INTNETNETWORK *PINTNETNETWORK;
238
239/** The size of the buffer INTNETNETWORK::pbTmp points at. */
240#define INTNETNETWORK_TMP_SIZE 2048
241
242
243/**
244 * Internal networking instance.
245 */
246typedef struct INTNET
247{
248 /** Mutex protecting the network creation, opening and destruction.
249 * (This means all operations affecting the pNetworks list.) */
250 RTSEMFASTMUTEX FastMutex;
251 /** List of networks. Protected by INTNET::Spinlock. */
252 PINTNETNETWORK volatile pNetworks;
253 /** Handle table for the interfaces. */
254 RTHANDLETABLE hHtIfs;
255} INTNET;
256
257
258
259/*******************************************************************************
260* Internal Functions *
261*******************************************************************************/
262static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis);
263static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis);
264static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis);
265static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis);
266
267
268/**
269 * Initializes a scatter / gather buffer from a simple linear buffer.
270 *
271 * @returns Pointer to the start of the frame.
272 * @param pSG Pointer to the scatter / gather structure.
273 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
274 * @param pvFrame Pointer to the frame
275 * @param cbFrame The size of the frame.
276 */
277DECLINLINE(void) intnetR0SgInitTemp(PINTNETSG pSG, void *pvFrame, uint32_t cbFrame)
278{
279 pSG->pvOwnerData = NULL;
280 pSG->pvUserData = NULL;
281 pSG->pvUserData2 = NULL;
282 pSG->cbTotal = cbFrame;
283 pSG->cUsers = 1;
284 pSG->fFlags = INTNETSG_FLAGS_TEMP;
285 pSG->cSegsAlloc = 1;
286 pSG->cSegsUsed = 1;
287 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
288 pSG->aSegs[0].pv = pvFrame;
289 pSG->aSegs[0].cb = cbFrame;
290}
291
292
293/**
294 * Initializes a scatter / gather buffer from a internal networking packet.
295 *
296 * @returns Pointer to the start of the frame.
297 * @param pSG Pointer to the scatter / gather structure.
298 * (The pvOwnerData, fFlags, cUsers, and cSegsAlloc members are left untouched.)
299 * @param pHdr Pointer to the packet header.
300 * @param pBuf The buffer the header is within. Only used in strict builds.
301 */
302DECLINLINE(void) intnetR0SgInitFromPkt(PINTNETSG pSG, PCINTNETHDR pPktHdr, PCINTNETBUF pBuf)
303{
304 pSG->cSegsUsed = 1;
305 pSG->cbTotal = pSG->aSegs[0].cb = pPktHdr->cbFrame;
306 pSG->aSegs[0].pv = INTNETHdrGetFramePtr(pPktHdr, pBuf);
307 pSG->aSegs[0].Phys = NIL_RTHCPHYS;
308}
309
310
311/**
312 * Worker for intnetR0SgWritePart that deals with the case where the
313 * request doesn't fit into the first segment.
314 *
315 * @returns true, unless the request or SG invalid.
316 * @param pSG The SG list to write to.
317 * @param off Where to start writing (offset into the SG).
318 * @param cb How much to write.
319 * @param pvBuf The buffer to containing the bits to write.
320 */
321static bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
322{
323 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
324 return false;
325
326 /*
327 * Skip ahead to the segment where off starts.
328 */
329 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
330 unsigned iSeg = 0;
331 while (off > pSG->aSegs[iSeg].cb)
332 {
333 off -= pSG->aSegs[iSeg++].cb;
334 AssertReturn(iSeg < cSegs, false);
335 }
336
337 /*
338 * Copy the data, hoping that it's all from one segment...
339 */
340 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
341 if (cbCanCopy >= cb)
342 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
343 else
344 {
345 /* copy the portion in the current segment. */
346 memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
347 cb -= cbCanCopy;
348
349 /* copy the portions in the other segments. */
350 do
351 {
352 pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
353 iSeg++;
354 AssertReturn(iSeg < cSegs, false);
355
356 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
357 memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
358
359 cb -= cbCanCopy;
360 } while (cb > 0);
361 }
362
363 return true;
364}
365
366
367/**
368 * Writes to a part of an SG.
369 *
370 * @returns true on success, false on failure (out of bounds).
371 * @param pSG The SG list to write to.
372 * @param off Where to start writing (offset into the SG).
373 * @param cb How much to write.
374 * @param pvBuf The buffer to containing the bits to write.
375 */
376DECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
377{
378 Assert(off + cb > off);
379
380 /* The optimized case. */
381 if (RT_LIKELY( pSG->cSegsUsed == 1
382 || pSG->aSegs[0].cb >= off + cb))
383 {
384 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
385 memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
386 return true;
387 }
388 return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
389}
390
391
392/**
393 * Reads a byte from a SG list.
394 *
395 * @returns The byte on success. 0xff on failure.
396 * @param pSG The SG list to read.
397 * @param off The offset (into the SG) off the byte.
398 */
399DECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
400{
401 if (RT_LIKELY(pSG->aSegs[0].cb > off))
402 return ((uint8_t const *)pSG->aSegs[0].pv)[off];
403
404 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
405 for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
406 {
407 if (pSG->aSegs[iSeg].cb > 0)
408 return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
409 off -= pSG->aSegs[iSeg].cb;
410 }
411 return false;
412}
413
414
415/**
416 * Worker for intnetR0SgReadPart that deals with the case where the
417 * requested data isn't in the first segment.
418 *
419 * @returns true, unless the SG is invalid.
420 * @param pSG The SG list to read.
421 * @param off Where to start reading (offset into the SG).
422 * @param cb How much to read.
423 * @param pvBuf The buffer to read into.
424 */
425static bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
426{
427 if (RT_UNLIKELY(off + cb > pSG->cbTotal))
428 return false;
429
430 /*
431 * Skip ahead to the segment where off starts.
432 */
433 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
434 unsigned iSeg = 0;
435 while (off > pSG->aSegs[iSeg].cb)
436 {
437 off -= pSG->aSegs[iSeg++].cb;
438 AssertReturn(iSeg < cSegs, false);
439 }
440
441 /*
442 * Copy the data, hoping that it's all from one segment...
443 */
444 uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
445 if (cbCanCopy >= cb)
446 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
447 else
448 {
449 /* copy the portion in the current segment. */
450 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
451 cb -= cbCanCopy;
452
453 /* copy the portions in the other segments. */
454 do
455 {
456 pvBuf = (uint8_t *)pvBuf + cbCanCopy;
457 iSeg++;
458 AssertReturn(iSeg < cSegs, false);
459
460 cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
461 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
462
463 cb -= cbCanCopy;
464 } while (cb > 0);
465 }
466
467 return true;
468}
469
470
471/**
472 * Reads a part of an SG into a buffer.
473 *
474 * @returns true on success, false on failure (out of bounds).
475 * @param pSG The SG list to read.
476 * @param off Where to start reading (offset into the SG).
477 * @param cb How much to read.
478 * @param pvBuf The buffer to read into.
479 */
480DECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
481{
482 Assert(off + cb > off);
483
484 /* The optimized case. */
485 if (RT_LIKELY( pSG->cSegsUsed == 1
486 || pSG->aSegs[0].cb >= off + cb))
487 {
488 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
489 memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
490 return true;
491 }
492 return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
493}
494
495
496/**
497 * Reads an entire SG into a fittingly size buffer.
498 *
499 * @param pSG The SG list to read.
500 * @param pvBuf The buffer to read into (at least pSG->cbTotal in size).
501 */
502DECLINLINE(void) intnetR0SgRead(PCINTNETSG pSG, void *pvBuf)
503{
504 if (pSG->cSegsUsed == 1)
505 {
506 Assert(pSG->cbTotal == pSG->aSegs[0].cb);
507 memcpy(pvBuf, pSG->aSegs[0].pv, pSG->cbTotal);
508 }
509 else
510 {
511 uint8_t *pbDst = (uint8_t *)pvBuf;
512 unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
513 for (unsigned iSeg = 0; iSeg < cSegs; iSeg++)
514 {
515 uint32_t cbSeg = pSG->aSegs[iSeg].cb;
516 Assert(cbSeg <= pSG->cbTotal && (uintptr_t)(pbDst - (uint8_t *)pvBuf) + cbSeg <= pSG->cbTotal);
517 memcpy(pbDst, pSG->aSegs[iSeg].pv, cbSeg);
518 pbDst += cbSeg;
519 }
520 }
521}
522
523
524
525
526
527
528
529/**
530 * Retain an interface.
531 *
532 * @returns VBox status code, can assume success in most situations.
533 * @param pIf The interface instance.
534 * @param pSession The current session.
535 */
536DECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
537{
538 int rc = SUPR0ObjAddRef(pIf->pvObj, pSession);
539 AssertRCReturn(rc, rc);
540 return VINF_SUCCESS;
541}
542
543
544/**
545 * Release an interface previously retained by intnetR0IfRetain or
546 * by handle lookup/freeing.
547 *
548 * @returns VBox status code, can assume success in most situations.
549 * @param pIf The interface instance.
550 * @param pSession The current session.
551 */
552DECLINLINE(void) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
553{
554 int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
555 AssertRC(rc);
556}
557
558
559/**
560 * RTHandleCreateEx callback that retains an object in the
561 * handle table before returning it.
562 *
563 * (Avoids racing the freeing of the handle.)
564 *
565 * @returns VBox status code.
566 * @param hHandleTable The handle table (ignored).
567 * @param pvObj The object (INTNETIF).
568 * @param pvCtx The context (SUPDRVSESSION).
569 * @param pvUser The user context (ignored).
570 */
571static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
572{
573 NOREF(pvUser);
574 NOREF(hHandleTable);
575 PINTNETIF pIf = (PINTNETIF)pvObj;
576 if (pIf->hIf != INTNET_HANDLE_INVALID) /* Don't try retain it if called from intnetR0IfDestruct. */
577 return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
578 return VINF_SUCCESS;
579}
580
581
582
583
584
585
586/**
587 * Checks if the IPv4 address is a broadcast address.
588 * @returns true/false.
589 * @param Addr The address, network endian.
590 */
591DECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
592{
593 /* Just check for 255.255.255.255 atm. */
594 return Addr.u == UINT32_MAX;
595}
596
597
598/**
599 * Checks if the IPv4 address is a good interface address.
600 * @returns true/false.
601 * @param Addr The address, network endian.
602 */
603DECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
604{
605 /* Usual suspects. */
606 if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
607 || Addr.au8[0] == 0) /* Current network, can be used as source address. */
608 return false;
609
610 /* Unusual suspects. */
611 if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
612 || (Addr.au8[0] & 0xf0 == 224) /* Multicast */
613 ))
614 return false;
615 return true;
616}
617
618
619/**
620 * Gets the address size of a network layer type.
621 *
622 * @returns size in bytes.
623 * @param enmType The type.
624 */
625DECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
626{
627 switch (enmType)
628 {
629 case kIntNetAddrType_IPv4: return 4;
630 case kIntNetAddrType_IPv6: return 16;
631 case kIntNetAddrType_IPX: return 4 + 6;
632 default: AssertFailedReturn(0);
633 }
634}
635
636
637/**
638 * Compares two address to see if they are equal, assuming naturally align structures.
639 *
640 * @returns true if equal, false if not.
641 * @param pAddr1 The first address.
642 * @param pAddr2 The second address.
643 * @param cbAddr The address size.
644 */
645DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
646{
647 switch (cbAddr)
648 {
649 case 4: /* IPv4 */
650 return pAddr1->au32[0] == pAddr2->au32[0];
651 case 16: /* IPv6 */
652 return pAddr1->au64[0] == pAddr2->au64[0]
653 && pAddr1->au64[1] == pAddr2->au64[1];
654 case 10: /* IPX */
655 return pAddr1->au64[0] == pAddr2->au64[0]
656 && pAddr1->au16[4] == pAddr2->au16[4];
657 default:
658 AssertFailedReturn(false);
659 }
660}
661
662
663/**
664 * Worker for intnetR0IfAddrCacheLookup that performs the lookup
665 * in the remaining cache entries after the caller has check the
666 * most likely ones.
667 *
668 * @returns -1 if not found, the index of the cache entry if found.
669 * @param pCache The cache.
670 * @param pAddr The address.
671 * @param cbAddr The address size (optimization).
672 */
673static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
674{
675 unsigned i = pCache->cEntries - 2;
676 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
677 while (i >= 1)
678 {
679 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
680 return i;
681 pbEntry -= pCache->cbEntry;
682 i--;
683 }
684
685 return -1;
686}
687
688/**
689 * Lookup an address in a cache without any expectations.
690 *
691 * @returns -1 if not found, the index of the cache entry if found.
692 * @param pCache The cache.
693 * @param pAddr The address.
694 * @param cbAddr The address size (optimization).
695 */
696DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
697{
698 Assert(pCache->cbAddress == cbAddr);
699
700 /*
701 * The optimized case is when there is one cache entry and
702 * it doesn't match.
703 */
704 unsigned i = pCache->cEntries;
705 if ( i > 0
706 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
707 return 0;
708 if (i <= 1)
709 return -1;
710
711 /*
712 * Check the last entry.
713 */
714 i--;
715 if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
716 return i;
717 if (i <= 1)
718 return -1;
719
720 return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
721}
722
723
724/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
725DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
726{
727 /** @todo implement this. */
728 return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
729}
730
731
732/**
733 * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
734 * the lookup in the remaining cache entries after the caller
735 * has check the most likely ones.
736 *
737 * The routine is expecting not to find the address.
738 *
739 * @returns -1 if not found, the index of the cache entry if found.
740 * @param pCache The cache.
741 * @param pAddr The address.
742 * @param cbAddr The address size (optimization).
743 */
744static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
745{
746 /*
747 * Perform a full table lookup.
748 */
749 unsigned i = pCache->cEntries - 2;
750 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
751 while (i >= 1)
752 {
753 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
754 return i;
755 pbEntry -= pCache->cbEntry;
756 i--;
757 }
758
759 return -1;
760}
761
762
763/**
764 * Lookup an address in a cache expecting not to find it.
765 *
766 * @returns -1 if not found, the index of the cache entry if found.
767 * @param pCache The cache.
768 * @param pAddr The address.
769 * @param cbAddr The address size (optimization).
770 */
771DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
772{
773 Assert(pCache->cbAddress == cbAddr);
774
775 /*
776 * The optimized case is when there is one cache entry and
777 * it doesn't match.
778 */
779 unsigned i = pCache->cEntries;
780 if (RT_UNLIKELY( i > 0
781 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
782 return 0;
783 if (RT_LIKELY(i <= 1))
784 return -1;
785
786 /*
787 * Then check the last entry and return if there are just two cache entries.
788 */
789 i--;
790 if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
791 return i;
792 if (i <= 1)
793 return -1;
794
795 return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
796}
797
798
799/**
800 * Deletes a specific cache entry.
801 *
802 * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
803 *
804 * @param pIf The interface (for logging).
805 * @param pCache The cache.
806 * @param iEntry The entry to delete.
807 * @param pszMsg Log message.
808 */
809static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
810{
811 Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 type=%d #%d %.*Rhxs %s\n", pIf->hIf,
812 (int)(intptr_t)(pCache - &pIf->aAddrCache[0]), iEntry, pCache->cbAddress,
813 pCache->pbEntries + iEntry * pCache->cbEntry, pszMsg));
814 AssertReturnVoid(iEntry < pCache->cEntries);
815 pCache->cEntries--;
816 if (iEntry < pCache->cEntries)
817 memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
818 pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
819 (pCache->cEntries - iEntry) * pCache->cbEntry);
820}
821
822
823/**
824 * Deletes an address from the cache, assuming it isn't actually in the cache.
825 *
826 * @param pIf The interface (for logging).
827 * @param pCache The cache.
828 * @param pAddr The address.
829 * @param cbAddr The address size (optimization).
830 */
831DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
832{
833 int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
834 if (RT_UNLIKELY(i >= 0))
835 intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
836}
837
838
839/**
840 * Deletes the address from all the interface caches.
841 *
842 * This is used to remove stale entries that has been reassigned to
843 * other machines on the network.
844 *
845 * @param pNetwork The network.
846 * @param pAddr The address.
847 * @param enmType The address type.
848 * @param cbAddr The address size (optimization).
849 * @param pszMsg Log message.
850 */
851DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
852 uint8_t const cbAddr, const char *pszMsg)
853{
854 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
855 {
856 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
857 if (RT_UNLIKELY(i >= 0))
858 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
859 }
860}
861
862
863/**
864 * Deletes the address from all the interface caches except the specified one.
865 *
866 * This is used to remove stale entries that has been reassigned to
867 * other machines on the network.
868 *
869 * @param pNetwork The network.
870 * @param pAddr The address.
871 * @param enmType The address type.
872 * @param cbAddr The address size (optimization).
873 * @param pszMsg Log message.
874 */
875DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
876 INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
877{
878 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
879 if (pIf != pIfSender)
880 {
881 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
882 if (RT_UNLIKELY(i >= 0))
883 intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
884 }
885}
886
887
888/**
889 * Lookup an address on the network, returning the (first) interface
890 * having it in its address cache.
891 *
892 * @returns Pointer to the interface on success, NULL if not found.
893 * @param pNetwork The network.
894 * @param pAddr The address to lookup.
895 * @param enmType The address type.
896 * @param cbAddr The size of the address.
897 */
898DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
899{
900 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
901 {
902 int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
903 if (i >= 0)
904 return pIf;
905 }
906 return NULL;
907}
908
909
910/**
911 * Adds an address to the cache, the caller is responsible for making sure it'
912 * s not already in the cache.
913 *
914 * @param pIf The interface (for logging).
915 * @param pCache The address cache.
916 * @param pAddr The address.
917 * @param pszMsg log message.
918 */
919static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, const char *pszMsg)
920{
921 if (!pCache->cEntriesAlloc)
922 {
923 /* Allocate the first array */
924 pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cbEntry * 16);
925 if (!pCache->pbEntries)
926 return;
927 pCache->cEntriesAlloc = 16;
928 }
929 else if (pCache->cEntries >= pCache->cEntriesAlloc)
930 {
931 bool fReplace = true;
932 if (pCache->cEntriesAlloc < 64)
933 {
934 uint8_t cEntriesAlloc = pCache->cEntriesAlloc + 16;
935 void *pvNew = RTMemRealloc(pCache->pbEntries, pCache->cbEntry * cEntriesAlloc);
936 if (pvNew)
937 {
938 pCache->pbEntries = (uint8_t *)pvNew;
939 pCache->cEntriesAlloc = cEntriesAlloc;
940 fReplace = false;
941 }
942 }
943 if (fReplace)
944 {
945 /* simple FIFO, might consider usage/ageing here? */
946 Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
947 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
948 memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
949 pCache->cEntries--;
950 }
951 }
952
953 /*
954 * Add the new entry to the end of the array.
955 */
956 uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
957 memcpy(pbEntry, pAddr, pCache->cbAddress);
958 memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
959 Log(("intnetR0IfAddrCacheAddIt: type=%d added #%d %.*Rhxs %s\n",
960 (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cEntries,
961 pCache->cbAddress, pAddr, pszMsg));
962 pCache->cEntries++;
963 Assert(pCache->cEntries <= pCache->cEntriesAlloc);
964}
965
966
967/**
968 * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
969 *
970 * @param pIf The interface (for logging).
971 * @param pCache The address cache.
972 * @param pAddr The address.
973 * @param cbAddr The size of the address (optimization).
974 * @param pszMsg Log message.
975 */
976static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
977{
978 /*
979 * Check all but the first and last entries, the caller
980 * has already checked those.
981 */
982 unsigned cLeft = pCache->cEntries;
983 uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
984 while (--cLeft > 1)
985 {
986 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
987 {
988 /** @todo usage/ageing? */
989 return;
990 }
991 pbEntry += pCache->cbEntry;
992 cLeft--;
993 }
994
995 /*
996 * Not found, add it.
997 */
998 intnetR0IfAddrCacheAddIt(pIf, pCache, pAddr, pszMsg);
999}
1000
1001
1002/**
1003 * Adds an address to the cache if it's not already there.
1004 *
1005 * @param pIf The interface (for logging).
1006 * @param pCache The address cache.
1007 * @param pAddr The address.
1008 * @param cbAddr The size of the address (optimization).
1009 * @param pszMsg Log message.
1010 */
1011DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
1012{
1013 Assert(pCache->cbAddress == cbAddr);
1014
1015 /*
1016 * The optimized case is when the address the first cache entry.
1017 */
1018 unsigned i = pCache->cEntries;
1019 if (RT_LIKELY( i > 0
1020 && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
1021 {
1022 /** @todo usage/ageing? */
1023 return;
1024 }
1025 if (i <= 1)
1026 return;
1027
1028 /*
1029 * And the case where it's the last entry.
1030 */
1031 i--;
1032 if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
1033 {
1034 /** @todo usage/ageing? */
1035 return;
1036 }
1037 if (i <= 1)
1038 return;
1039
1040 intnetR0IfAddrCacheAddSlow(pIf, pCache, pAddr, cbAddr, pszMsg);
1041}
1042
1043
1044#ifdef INTNET_WITH_DHCP_SNOOPING
1045
1046/**
1047 * Snoops IP assignments and releases from the DHCPv4 traffic.
1048 *
1049 * The caller is responsible for making sure this traffic between the
1050 * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet
1051 * need not be validated beyond the ports.
1052 *
1053 * @param pNetwork The network this frame was seen on.
1054 * @param pIpHdr Pointer to a valid IP header. This is for pseudo
1055 * header validation, so only the minimum header size
1056 * needs to be available and valid here.
1057 * @param pUdpHdr Pointer to the UDP header in the frame.
1058 * @param cbUdpPkt What's left of the frame when starting at the UDP header.
1059 */
1060static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
1061{
1062 /** @todo later */
1063}
1064
1065
1066static void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
1067{
1068}
1069
1070#endif /* INTNET_WITH_DHCP_SNOOPING */
1071
1072
1073/**
1074 * Snoops up source addresses from ARP requests and purge these
1075 * from the address caches.
1076 *
1077 * The purpose of this purging is to get rid of stale addresses.
1078 *
1079 * @param pNetwork The network the this frame was seen on.
1080 * @param pSG The gather list for the frame.
1081 */
1082static void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
1083{
1084 /*
1085 * Check the minimum size first.
1086 */
1087 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
1088 return;
1089
1090 /*
1091 * Copy to temporary buffer if necessary.
1092 */
1093 size_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
1094 PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1095 if ( pSG->cSegsUsed != 1
1096 && pSG->aSegs[0].cb < cbPacket)
1097 {
1098 if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
1099 != (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
1100 && !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
1101 return;
1102 pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
1103 }
1104
1105 /*
1106 * Ignore packets which doesn't interest us or we perceive as malformed.
1107 */
1108 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1109 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1110 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1111 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1112 return;
1113 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1114 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1115 && ar_oper != RTNET_ARPOP_REPLY))
1116 {
1117 Log6(("ts-ar: op=%#x\n", ar_oper));
1118 return;
1119 }
1120
1121 /*
1122 * Delete the source address if it's OK.
1123 */
1124 if ( !(pArpIPv4->ar_sha.au8[0] & 1)
1125 && ( pArpIPv4->ar_sha.au16[0]
1126 || pArpIPv4->ar_sha.au16[1]
1127 || pArpIPv4->ar_sha.au16[2])
1128 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
1129 {
1130 Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
1131 pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
1132 intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
1133 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
1134 }
1135}
1136
1137
1138#ifdef INTNET_WITH_DHCP_SNOOPING
1139/**
1140 * Snoop up addresses from ARP and DHCP traffic from frames comming
1141 * over the trunk connection.
1142 *
1143 * The caller is responsible for do some basic filtering before calling
1144 * this function.
1145 * For IPv4 this means checking against the minimum DHCPv4 frame size.
1146 *
1147 * @param pNetwork The network.
1148 * @param pSG The SG list for the frame.
1149 * @param EtherType The Ethertype of the frame.
1150 */
1151static void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
1152{
1153 switch (EtherType)
1154 {
1155 case RTNET_ETHERTYPE_IPV4:
1156 {
1157 Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
1158/** @todo optimize for the case where we can expect both the IP and UDP header to be in the first segment. */
1159
1160 /* check if the protocol is UDP */
1161 if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
1162 != RTNETIPV4_PROT_UDP)
1163 return;
1164
1165 /* get the TCP header length */
1166 uint8_t b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
1167 uint32_t cbIpHdr = (b & 0x0f) * 4;
1168 if (cbIpHdr < RTNETIPV4_MIN_LEN)
1169 return;
1170
1171 /* get the lower byte of the UDP source port number. */
1172 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
1173 if ( b != RTNETIPV4_PORT_BOOTPS
1174 && b != RTNETIPV4_PORT_BOOTPC)
1175 return;
1176 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
1177 if (b)
1178 return;
1179 intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
1180 break;
1181 }
1182
1183 case RTNET_ETHERTYPE_IPV6:
1184 {
1185 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1186 * need to be edited. Check out how NDP works... */
1187 break;
1188 }
1189
1190 case RTNET_ETHERTYPE_ARP:
1191 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
1192 break;
1193 }
1194}
1195#endif /* INTNET_WITH_DHCP_SNOOPING */
1196
1197
1198/**
1199 * Deals with an IPv4 packet.
1200 *
1201 * This will fish out the source IP address and add it to the cache.
1202 * Then it will look for DHCPRELEASE requests (?) and anything else
1203 * that we migh find useful later.
1204 *
1205 * @param pIf The interface that's sending the frame.
1206 * @param pIpHdr Pointer to the IPv4 header in the frame.
1207 * @param cbPacket The size of the packet, or more correctly the
1208 * size of the frame without the ethernet header.
1209 */
1210static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket)
1211{
1212 /*
1213 * Check the header size first to prevent access invalid data.
1214 */
1215 if (cbPacket < RTNETIPV4_MIN_LEN)
1216 return;
1217 uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
1218 if ( cbHdr < RT_UOFFSETOF(RTNETIPV4, ip_options)
1219 || cbPacket < cbHdr)
1220 return;
1221
1222 /*
1223 * Ignore non good IP address (like broadcast and my network),
1224 * also skip packets containing address that are already in the
1225 * cache. Don't ignore potential DHCP traffic though.
1226 */
1227 bool fValidatedIpHdr = false;
1228 RTNETADDRU Addr;
1229 Addr.IPv4 = pIpHdr->ip_src;
1230 if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
1231 && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
1232 {
1233 /*
1234 * Got a candidate, check that the IP header is valid before adding it.
1235 */
1236 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1237 {
1238 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
1239 return;
1240 }
1241 intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
1242 fValidatedIpHdr = true;
1243 }
1244
1245#ifdef INTNET_WITH_DHCP_SNOOPING
1246 /*
1247 * Check for potential DHCP packets.
1248 */
1249 if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
1250 && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
1251 {
1252 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
1253 if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
1254 || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
1255 && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
1256 || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
1257 {
1258 if ( fValidatedIpHdr
1259 || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1260 intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
1261 else
1262 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
1263 }
1264 }
1265#endif /* INTNET_WITH_DHCP_SNOOPING */
1266}
1267
1268
1269/**
1270 * Snoop up source addresses from an ARP request or reply.
1271 *
1272 * @param pIf The interface that's sending the frame.
1273 * @param pHdr The ARP header.
1274 * @param cbPacket The size of the packet (migth be larger than the ARP
1275 * request 'cause of min ethernet frame size).
1276 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1277 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1278 */
1279static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
1280{
1281 /*
1282 * Ignore packets which doesn't interest us or we perceive as malformed.
1283 */
1284 if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
1285 return;
1286 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1287 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1288 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1289 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1290 return;
1291 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1292 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1293 && ar_oper != RTNET_ARPOP_REPLY))
1294 {
1295 Log6(("ar_oper=%#x\n", ar_oper));
1296 return;
1297 }
1298
1299 /*
1300 * Tag the SG as ARP IPv4 for later editing, then check for addresses
1301 * which can be removed or added to the address cache of the sender.
1302 */
1303 *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
1304
1305 if ( ar_oper == RTNET_ARPOP_REPLY
1306 && !(pArpIPv4->ar_tha.au8[0] & 1)
1307 && ( pArpIPv4->ar_tha.au16[0]
1308 || pArpIPv4->ar_tha.au16[1]
1309 || pArpIPv4->ar_tha.au16[2])
1310 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
1311 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1312 (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
1313
1314 if ( !memcmp(&pArpIPv4->ar_sha, &pIf->Mac, sizeof(RTMAC))
1315 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
1316 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1317 (PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
1318}
1319
1320
1321
1322/**
1323 * Checks packets send by a normal interface for new network
1324 * layer addresses.
1325 *
1326 * @param pIf The interface that's sending the frame.
1327 * @param pbFrame The frame.
1328 * @param cbFrame The size of the frame.
1329 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1330 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1331 */
1332static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, uint16_t *pfSgFlags)
1333{
1334 /*
1335 * Fish out the ethertype and look for stuff we can handle.
1336 */
1337 if (cbFrame <= sizeof(RTNETETHERHDR))
1338 return;
1339 cbFrame -= sizeof(RTNETETHERHDR);
1340
1341 uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
1342 switch (EtherType)
1343 {
1344 case RTNET_ETHERTYPE_IPV4:
1345 intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
1346 break;
1347#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
1348 case RTNET_ETHERTYPE_IPV6:
1349 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1350 * need to be edited. Check out how NDP works... */
1351 intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1352 break;
1353#endif
1354#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
1355 case RTNET_ETHERTYPE_IPX_1:
1356 case RTNET_ETHERTYPE_IPX_2:
1357 case RTNET_ETHERTYPE_IPX_3:
1358 intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1359 break;
1360#endif
1361 case RTNET_ETHERTYPE_ARP:
1362 intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1363 break;
1364 }
1365}
1366
1367
1368#ifdef IN_INTNET_TESTCASE
1369/**
1370 * Reads the next frame in the buffer.
1371 * The caller is responsible for ensuring that there is a valid frame in the buffer.
1372 *
1373 * @returns Size of the frame in bytes.
1374 * @param pBuf The buffer.
1375 * @param pRingBuff The ring buffer to read from.
1376 * @param pvFrame Where to put the frame. The caller is responsible for
1377 * ensuring that there is sufficient space for the frame.
1378 */
1379static unsigned intnetR0RingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
1380{
1381 Assert(pRingBuf->offRead < pBuf->cbBuf);
1382 Assert(pRingBuf->offRead >= pRingBuf->offStart);
1383 Assert(pRingBuf->offRead < pRingBuf->offEnd);
1384 uint32_t offRead = pRingBuf->offRead;
1385 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offRead);
1386 const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
1387 unsigned cb = pHdr->cbFrame;
1388 memcpy(pvFrame, pvFrameIn, cb);
1389
1390 /* skip the frame */
1391 offRead += pHdr->offFrame + cb;
1392 offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
1393 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
1394 if (offRead >= pRingBuf->offEnd)
1395 offRead = pRingBuf->offStart;
1396 ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
1397 return cb;
1398}
1399#endif /* IN_INTNET_TESTCASE */
1400
1401
1402/**
1403 * Writes a frame packet to the buffer.
1404 *
1405 * @returns VBox status code.
1406 * @param pBuf The buffer.
1407 * @param pRingBuf The ring buffer to read from.
1408 * @param pSG The gather list.
1409 * @param pNewDstMac Set the destination MAC address to the address if specified.
1410 */
1411static int intnetR0RingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
1412{
1413 /*
1414 * Validate input.
1415 */
1416 AssertPtr(pBuf);
1417 AssertPtr(pRingBuf);
1418 AssertPtr(pSG);
1419 Assert(pSG->cbTotal >= sizeof(RTMAC) * 2);
1420 uint32_t offWrite = pRingBuf->offWrite;
1421 Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
1422 uint32_t offRead = pRingBuf->offRead;
1423 Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
1424
1425 const uint32_t cb = RT_ALIGN_32(pSG->cbTotal, sizeof(INTNETHDR));
1426 if (offRead <= offWrite)
1427 {
1428 /*
1429 * Try fit it all before the end of the buffer.
1430 */
1431 if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
1432 {
1433 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1434 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1435 pHdr->cbFrame = pSG->cbTotal;
1436 pHdr->offFrame = sizeof(INTNETHDR);
1437
1438 intnetR0SgRead(pSG, pHdr + 1);
1439 if (pNewDstMac)
1440 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1441
1442 offWrite += cb + sizeof(INTNETHDR);
1443 Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
1444 if (offWrite >= pRingBuf->offEnd)
1445 offWrite = pRingBuf->offStart;
1446 Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
1447 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1448 return VINF_SUCCESS;
1449 }
1450
1451 /*
1452 * Try fit the frame at the start of the buffer.
1453 * (The header fits before the end of the buffer because of alignment.)
1454 */
1455 AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
1456 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
1457 {
1458 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1459 void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
1460 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1461 pHdr->cbFrame = pSG->cbTotal;
1462 pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
1463
1464 intnetR0SgRead(pSG, pvFrameOut);
1465 if (pNewDstMac)
1466 ((PRTNETETHERHDR)pvFrameOut)->DstMac = *pNewDstMac;
1467
1468 offWrite = pRingBuf->offStart + cb;
1469 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1470 Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
1471 return VINF_SUCCESS;
1472 }
1473 }
1474 /*
1475 * The reader is ahead of the writer, try fit it into that space.
1476 */
1477 else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
1478 {
1479 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1480 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1481 pHdr->cbFrame = pSG->cbTotal;
1482 pHdr->offFrame = sizeof(INTNETHDR);
1483
1484 intnetR0SgRead(pSG, pHdr + 1);
1485 if (pNewDstMac)
1486 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1487
1488 offWrite += cb + sizeof(INTNETHDR);
1489 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1490 Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
1491 return VINF_SUCCESS;
1492 }
1493
1494 /* (it didn't fit) */
1495 /** @todo stats */
1496 return VERR_BUFFER_OVERFLOW;
1497}
1498
1499
1500/**
1501 * Sends a frame to a specific interface.
1502 *
1503 * @param pIf The interface.
1504 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1505 * @param pSG The gather buffer which data is being sent to the interface.
1506 * @param pNewDstMac Set the destination MAC address to the address if specified.
1507 */
1508static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
1509{
1510// LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
1511 int rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1512 if (RT_SUCCESS(rc))
1513 {
1514 pIf->cYields = 0;
1515 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1516 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1517 RTSemEventSignal(pIf->Event);
1518 return;
1519 }
1520
1521 Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
1522
1523#if 0 /* This is bad stuff now as we're blocking while locking down the network.
1524 we really shouldn't delay the network traffic on the host just because
1525 some bugger isn't responding. Will have to deal with this in a different
1526 manner if required. */
1527 /*
1528 * Retry a few times, yielding the CPU in between.
1529 * But don't let a unresponsive VM harm performance, so give up after a couple of tries.
1530 */
1531 if ( pIf->fActive
1532 && pIf->cYields < 100)
1533 {
1534 unsigned cYields = 10;
1535#else
1536 /*
1537 * Scheduling hack, for unicore machines primarily.
1538 */
1539 if ( pIf->fActive
1540 && pIf->cYields < 4 /* just twice */
1541 && pIfSender /* but not if it's from the trunk */)
1542 {
1543 unsigned cYields = 2;
1544#endif
1545 while (--cYields > 0)
1546 {
1547 RTSemEventSignal(pIf->Event);
1548 RTThreadYield();
1549 rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1550 if (RT_SUCCESS(rc))
1551 {
1552 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
1553 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1554 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1555 RTSemEventSignal(pIf->Event);
1556 return;
1557 }
1558 pIf->cYields++;
1559 }
1560 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
1561 }
1562
1563 /* ok, the frame is lost. */
1564 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
1565 RTSemEventSignal(pIf->Event);
1566}
1567
1568
1569/**
1570 * Sends a frame down the trunk.
1571 *
1572 * The caller must own the network mutex, might be abandond temporarily.
1573 * The fTrunkLock parameter indicates whether the trunk lock is held.
1574 *
1575 * @param pThis The trunk.
1576 * @param pNetwork The network the frame is being sent to.
1577 * @param pIfSender The IF sending the frame. Used for MAC address checks in shared MAC mode.
1578 * @param fDst The destination flags.
1579 * @param pSG Pointer to the gather list.
1580 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1581 */
1582static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
1583 uint32_t fDst, PINTNETSG pSG, bool fTrunkLocked)
1584{
1585 /*
1586 * Quick sanity check.
1587 */
1588 AssertPtr(pThis);
1589 AssertPtr(pNetwork);
1590 AssertPtr(pSG);
1591 Assert(fDst);
1592 AssertReturnVoid(pThis->pIfPort);
1593
1594 /*
1595 * Edit the frame if we're sharing the MAC address with the host on the wire.
1596 *
1597 * If the frame is headed for both the host and the wire, we'll have to send
1598 * it to the host before making any modifications, and force the OS specific
1599 * backend to copy it. We do this by marking it as TEMP (which is always the
1600 * case right now).
1601 */
1602 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1603 && (fDst & INTNETTRUNKDIR_WIRE))
1604 {
1605 /* Dispatch it to the host before making changes. */
1606 if (fDst & INTNETTRUNKDIR_HOST)
1607 {
1608 Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
1609 intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG, fTrunkLocked);
1610 fDst &= ~INTNETTRUNKDIR_HOST;
1611 }
1612
1613 /* ASSUME frame from INTNETR0IfSend! */
1614 AssertReturnVoid(pSG->cSegsUsed == 1);
1615 AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
1616 AssertReturnVoid(fTrunkLocked);
1617 AssertReturnVoid(pIfSender);
1618 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
1619
1620 /*
1621 * Get the host mac address and update the ethernet header.
1622 *
1623 * The reason for caching it in the trunk structure is because
1624 * we cannot take the trunk out-bound semaphore when we make
1625 * edits in the intnetR0TrunkIfPortRecv path.
1626 */
1627 pThis->pIfPort->pfnGetMacAddress(pThis->pIfPort, &pThis->CachedMac);
1628 if (!memcmp(&pEthHdr->SrcMac, &pIfSender->Mac, sizeof(RTMAC)))
1629 pEthHdr->SrcMac = pThis->CachedMac;
1630
1631 /*
1632 * Deal with tags from the snooping phase.
1633 */
1634 if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
1635 {
1636 /*
1637 * APR IPv4: replace hardware (MAC) addresses because these end up
1638 * in ARP caches. So, if we don't the other machiens will
1639 * send the packets to the MAC address of the guest
1640 * instead of the one of the host, which won't work on
1641 * wireless of course...
1642 */
1643 PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
1644 if (!memcmp(&pArp->ar_sha, &pIfSender->Mac, sizeof(RTMAC)))
1645 {
1646 Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->CachedMac));
1647 pArp->ar_sha = pThis->CachedMac;
1648 }
1649 if (!memcmp(&pArp->ar_tha, &pIfSender->Mac, sizeof(RTMAC))) /* just in case... */
1650 {
1651 Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->CachedMac));
1652 pArp->ar_tha = pThis->CachedMac;
1653 }
1654 }
1655 //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
1656 //{ /// @todo move the editing into a different function
1657 //}
1658 }
1659
1660 /*
1661 * Temporarily leave the network lock while transmitting the frame.
1662 *
1663 * Note that we're relying on the out-bound lock to serialize threads down
1664 * in INTNETR0IfSend. It's theoretically possible for there to be race now
1665 * because I didn't implement async SG handling yet. Which is why we currently
1666 * require the trunk to be locked, well, one of the reasons.
1667 *
1668 * Another reason is that the intnetR0NetworkSendUnicast code may have to
1669 * call into the trunk interface component to do package switching.
1670 */
1671 AssertReturnVoid(fTrunkLocked); /* to be removed. */
1672
1673 int rc;
1674 if ( fTrunkLocked
1675 || intnetR0TrunkIfRetain(pThis))
1676 {
1677 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1678 AssertRC(rc);
1679 if (RT_SUCCESS(rc))
1680 {
1681 if ( fTrunkLocked
1682 || intnetR0TrunkIfOutLock(pThis))
1683 {
1684 rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
1685
1686 if (!fTrunkLocked)
1687 intnetR0TrunkIfOutUnlock(pThis);
1688 }
1689 else
1690 {
1691 AssertFailed();
1692 rc = VERR_SEM_DESTROYED;
1693 }
1694
1695 int rc2 = RTSemFastMutexRequest(pNetwork->FastMutex);
1696 AssertRC(rc2);
1697 }
1698
1699 if (!fTrunkLocked)
1700 intnetR0TrunkIfRelease(pThis);
1701 }
1702 else
1703 {
1704 AssertFailed();
1705 rc = VERR_SEM_DESTROYED;
1706 }
1707
1708 /** @todo failure statistics? */
1709 Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst));
1710}
1711
1712
1713/**
1714 * Edits an ARP packet arriving from the wire via the trunk connection.
1715 *
1716 * @param pNetwork The network the frame is being sent to.
1717 * @param pSG Pointer to the gather list for the frame.
1718 * The flags and data content may be updated.
1719 * @param pEthHdr Pointer to the ethernet header. This may also be
1720 * updated if it's a unicast...
1721 */
1722static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
1723{
1724 /*
1725 * Check the minimum size and get a linear copy of the thing to work on,
1726 * using the temporary buffer if necessary.
1727 */
1728 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
1729 return;
1730 PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1731 if ( pSG->cSegsUsed != 1
1732 && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
1733 {
1734 Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
1735 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
1736 return;
1737 pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
1738 pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
1739 }
1740
1741 /*
1742 * Ignore packets which doesn't interest us or we perceive as malformed.
1743 */
1744 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1745 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1746 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1747 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1748 return;
1749 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1750 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1751 && ar_oper != RTNET_ARPOP_REPLY))
1752 {
1753 Log6(("ar_oper=%#x\n", ar_oper));
1754 return;
1755 }
1756
1757 /* Tag it as ARP IPv4. */
1758 pSG->fFlags |= INTNETSG_FLAGS_ARP_IPV4;
1759
1760 /*
1761 * The thing we're interested in here is a reply to a query made by a guest
1762 * since we modified the MAC in the initial request the guest made.
1763 */
1764 if ( ar_oper == RTNET_ARPOP_REPLY
1765 && !memcmp(&pArpIPv4->ar_tha, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1766 {
1767 PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
1768 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
1769 if (pIf)
1770 {
1771 Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->Mac));
1772 pArpIPv4->ar_tha = pIf->Mac;
1773 if (!memcmp(&pEthHdr->DstMac, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1774 {
1775 Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1776 pEthHdr->DstMac = pIf->Mac;
1777 if ((void *)pEthHdr != pSG->aSegs[0].pv)
1778 intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->Mac);
1779 }
1780
1781 /* Write back the packet if we've been making changes to a buffered copy. */
1782 if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
1783 intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
1784 }
1785 }
1786}
1787
1788
1789/**
1790 * Sends a broadcast frame.
1791 *
1792 * The caller must own the network mutex, might be abandond temporarily.
1793 * When pIfSender is not NULL, the caller must also own the trunk semaphore.
1794 *
1795 * @returns true if it's addressed to someone on the network, otherwise false.
1796 * @param pNetwork The network the frame is being sent to.
1797 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1798 * @param fSrc The source flags. This 0 if it's not from the trunk.
1799 * @param pSG Pointer to the gather list.
1800 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1801 * @param pEthHdr Pointer to the ethernet header.
1802 */
1803static bool intnetR0NetworkSendBroadcast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
1804 PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
1805{
1806 /*
1807 * Check for ARP packets from the wire since we'll have to make
1808 * modification to them if we're sharing the MAC address with the host.
1809 */
1810 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1811 && (fSrc & INTNETTRUNKDIR_WIRE)
1812 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP)
1813 intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
1814
1815 /*
1816 * This is a broadcast or multicast address. For the present we treat those
1817 * two as the same - investigating multicast is left for later.
1818 *
1819 * Write the packet to all the interfaces and signal them.
1820 */
1821 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1822 if (pIf != pIfSender)
1823 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
1824
1825 /*
1826 * Unless the trunk is the origin, broadcast it to both the wire
1827 * and the host as well.
1828 */
1829 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
1830 if ( pIfSender
1831 && pTrunkIf)
1832 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked);
1833
1834 /*
1835 * Snoop address info from packet orginating from the trunk connection.
1836 */
1837 else if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1838 && !pIfSender)
1839 {
1840#ifdef INTNET_WITH_DHCP_SNOOPING
1841 uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
1842 if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
1843 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
1844 || (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4)) )
1845 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
1846#else
1847 if (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4))
1848 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
1849#endif
1850 }
1851
1852 return false; /* broadcast frames are never dropped */
1853}
1854
1855
1856/**
1857 * Sends a multicast frame.
1858 *
1859 * The caller must own the network mutex, might be abandond temporarily.
1860 *
1861 * @returns true if it's addressed to someone on the network, otherwise false.
1862 * @param pNetwork The network the frame is being sent to.
1863 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1864 * @param fSrc The source flags. This 0 if it's not from the trunk.
1865 * @param pSG Pointer to the gather list.
1866 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1867 * @param pEthHdr Pointer to the ethernet header.
1868 */
1869static bool intnetR0NetworkSendMulticast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
1870{
1871 /** @todo implement multicast */
1872 return intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, pEthHdr);
1873}
1874
1875
1876/**
1877 * Sends a unicast frame using the network layer address instead
1878 * of the link layer one.
1879 *
1880 * The caller must own the network mutex, might be abandond temporarily.
1881 *
1882 * @returns true if it's addressed to someone on the network, otherwise false.
1883 * @param pNetwork The network the frame is being sent to.
1884 * @param pSG Pointer to the gather list.
1885 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1886 * @param pEthHdr Pointer to the ethernet header.
1887 */
1888static bool intnetR0NetworkSendUnicastWithSharedMac(PINTNETNETWORK pNetwork, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
1889{
1890 /*
1891 * Extract the network address from the packet.
1892 */
1893 RTNETADDRU Addr;
1894 INTNETADDRTYPE enmAddrType;
1895 uint8_t cbAddr;
1896 switch (RT_BE2H_U16(pEthHdr->EtherType))
1897 {
1898 case RTNET_ETHERTYPE_IPV4:
1899 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
1900 {
1901 Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
1902 return false;
1903 }
1904 enmAddrType = kIntNetAddrType_IPv4;
1905 cbAddr = sizeof(Addr.IPv4);
1906 Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
1907 break;
1908
1909#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
1910 case RTNET_ETHERTYPE_IPV6
1911 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
1912 {
1913 Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
1914 return false;
1915 }
1916 enmAddrType = kIntNetAddrType_IPv6;
1917 cbAddr = sizeof(Addr.IPv6);
1918 break;
1919#endif
1920#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
1921 case RTNET_ETHERTYPE_IPX_1:
1922 case RTNET_ETHERTYPE_IPX_2:
1923 case RTNET_ETHERTYPE_IPX_3:
1924 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
1925 {
1926 Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
1927 return false;
1928 }
1929 enmAddrType = kIntNetAddrType_IPX;
1930 cbAddr = sizeof(Addr.IPX);
1931 break;
1932#endif
1933
1934 /*
1935 * Treat ARP is broadcast (it shouldn't end up here normally,
1936 * so it goes last in the switch).
1937 */
1938 case RTNET_ETHERTYPE_ARP:
1939 Log6(("intnetshareduni: ARP\n"));
1940 /** @todo revisit this broadcasting of unicast ARP frames! */
1941 return intnetR0NetworkSendBroadcast(pNetwork, NULL, INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked, pEthHdr);
1942
1943 /*
1944 * Unknown packets are sent do all interfaces that are in promiscuous mode.
1945 */
1946 default:
1947 {
1948 Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
1949 if (!(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC)))
1950 {
1951 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1952 if (pIf->fPromiscuous)
1953 {
1954 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1955 intnetR0IfSend(pIf, NULL, pSG, NULL);
1956 }
1957 }
1958 return false;
1959 }
1960 }
1961
1962 /*
1963 * Send it to interfaces with matching network addresses.
1964 */
1965 bool fExactIntNetRecipient = false;
1966 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1967 {
1968 bool fIt = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmAddrType], &Addr, cbAddr) >= 0;
1969 if ( fIt
1970 || ( pIf->fPromiscuous
1971 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))))
1972 {
1973 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1974 fExactIntNetRecipient |= fIt;
1975 intnetR0IfSend(pIf, NULL, pSG, fIt ? &pIf->Mac : NULL);
1976 }
1977 }
1978
1979#ifdef INTNET_WITH_DHCP_SNOOPING
1980 /*
1981 * Perform DHCP snooping.
1982 */
1983 if ( enmAddrType == kIntNetAddrType_IPv4
1984 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
1985 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
1986#endif /* INTNET_WITH_DHCP_SNOOPING */
1987
1988 return fExactIntNetRecipient;
1989}
1990
1991
1992/**
1993 * Sends a unicast frame.
1994 *
1995 * The caller must own the network mutex, might be abandond temporarily.
1996 *
1997 * @returns true if it's addressed to someone on the network, otherwise false.
1998 * @param pNetwork The network the frame is being sent to.
1999 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2000 * @param fSrc The source flags. This 0 if it's not from the trunk.
2001 * @param pSG Pointer to the gather list.
2002 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2003 * @param pEthHdr Pointer to the ethernet header.
2004 */
2005static bool intnetR0NetworkSendUnicast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
2006{
2007 /*
2008 * Only send to the interfaces with matching a MAC address.
2009 */
2010 bool fExactIntNetRecipient = false;
2011 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2012 {
2013 bool fIt = false;
2014 if ( ( !pIf->fMacSet
2015 || (fIt = !memcmp(&pIf->Mac, &pEthHdr->DstMac, sizeof(pIf->Mac))) )
2016 || ( pIf->fPromiscuous
2017 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))
2018 && pIf != pIfSender /* promiscuous mode: omit the sender */))
2019 {
2020 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2021 fExactIntNetRecipient |= fIt;
2022 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
2023 }
2024 }
2025
2026 /*
2027 * Send it to the trunk?
2028 * If we didn't find the recipient on the internal network the
2029 * frame will hit the wire.
2030 */
2031 uint32_t fDst = 0;
2032 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2033 if ( pIfSender
2034 && pTrunkIf
2035 && pTrunkIf->pIfPort)
2036 {
2037 Assert(!fSrc);
2038
2039 /* promiscuous checks first as they are cheaper than pfnIsHostMac. */
2040 if ( pTrunkIf->fPromiscuousWire
2041 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_WIRE | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_WIRE)) )
2042 fDst |= INTNETTRUNKDIR_WIRE;
2043 if ( !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC | INTNET_OPEN_FLAGS_IGNORE_PROMISC_TRUNK_HOST | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC_TRUNK_HOST))
2044 || pTrunkIf->pIfPort->pfnIsPromiscuous(pTrunkIf->pIfPort) )
2045 fDst |= INTNETTRUNKDIR_HOST;
2046
2047 if ( fDst != (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
2048 && !fExactIntNetRecipient /* if you have duplicate mac addresses, you're screwed. */ )
2049 {
2050 if (pTrunkIf->pIfPort->pfnIsHostMac(pTrunkIf->pIfPort, &pEthHdr->DstMac))
2051 fDst |= INTNETTRUNKDIR_HOST;
2052 else
2053 fDst |= INTNETTRUNKDIR_WIRE;
2054 }
2055
2056 if (fDst)
2057 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, fDst, pSG, fTrunkLocked);
2058 }
2059
2060 /* log it */
2061 if ( !fExactIntNetRecipient
2062 && !fDst
2063 && ( (pEthHdr->DstMac.au8[0] == 0x08 && pEthHdr->DstMac.au8[1] == 0x00 && pEthHdr->DstMac.au8[2] == 0x27)
2064 || (pEthHdr->SrcMac.au8[0] == 0x08 && pEthHdr->SrcMac.au8[1] == 0x00 && pEthHdr->SrcMac.au8[2] == 0x27)))
2065 Log2(("Dst=%.6Rhxs ??\n", &pEthHdr->DstMac));
2066
2067 return fExactIntNetRecipient;
2068}
2069
2070
2071/**
2072 * Sends a frame.
2073 *
2074 * This function will distribute the frame to the interfaces it is addressed to.
2075 * It will also update the MAC address of the sender.
2076 *
2077 * The caller must own the network mutex.
2078 *
2079 * @returns true if it's addressed to someone on the network, otherwise false.
2080 * @param pNetwork The network the frame is being sent to.
2081 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2082 * @param fSrc The source flags. This 0 if it's not from the trunk.
2083 * @param pSG Pointer to the gather list.
2084 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2085 */
2086static bool intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked)
2087{
2088 bool fRc = false;
2089
2090 /*
2091 * Assert reality.
2092 */
2093 AssertPtr(pNetwork);
2094 AssertPtrNull(pIfSender);
2095 Assert(pIfSender ? fSrc == 0 : fSrc != 0);
2096 Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
2097 AssertPtr(pSG);
2098 Assert(pSG->cSegsUsed >= 1);
2099 Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
2100 if (pSG->cbTotal < sizeof(RTNETETHERHDR))
2101 return fRc;
2102
2103 /*
2104 * Send statistics.
2105 */
2106 if (pIfSender)
2107 {
2108 STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
2109 STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, pSG->cbTotal);
2110 }
2111
2112 /*
2113 * Get the ethernet header (might theoretically involve multiple segments).
2114 */
2115 RTNETETHERHDR EthHdr;
2116 if (RT_LIKELY(pSG->aSegs[0].cb >= sizeof(EthHdr)))
2117 EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
2118 else
2119 {
2120 uint8_t *pbDst = (uint8_t *)&EthHdr;
2121 size_t cbLeft = sizeof(EthHdr);
2122 for (unsigned iSeg = 0; cbLeft && iSeg < pSG->cSegsUsed; iSeg++)
2123 {
2124 size_t cb = RT_MIN(cbLeft, pSG->aSegs[iSeg].cb);
2125 memcpy(pbDst, pSG->aSegs[iSeg].pv, cb);
2126 pbDst += cb;
2127 cbLeft -= cb;
2128 }
2129 AssertReturn(!cbLeft, false);
2130 }
2131 if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
2132 || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
2133 || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
2134 || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
2135 || EthHdr.DstMac.au8[0] == 0xff
2136 || EthHdr.SrcMac.au8[0] == 0xff)
2137 Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
2138 &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
2139
2140 /*
2141 * Inspect the header updating the mac address of the sender in the process.
2142 */
2143 if ( pIfSender
2144 && memcmp(&EthHdr.SrcMac, &pIfSender->Mac, sizeof(pIfSender->Mac)))
2145 {
2146 /** @todo stats */
2147 Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &EthHdr.SrcMac));
2148 pIfSender->Mac = EthHdr.SrcMac;
2149 pIfSender->fMacSet = true;
2150 }
2151
2152 /*
2153 * Distribute the frame.
2154 */
2155 if ( EthHdr.DstMac.au16[0] == 0xffff /* broadcast address. */
2156 && EthHdr.DstMac.au16[1] == 0xffff
2157 && EthHdr.DstMac.au16[2] == 0xffff)
2158 fRc = intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2159 else if (RT_UNLIKELY(EthHdr.DstMac.au8[0] & 1)) /* multicast address */
2160 fRc = intnetR0NetworkSendMulticast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2161 else if ( !(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2162 || !(fSrc & INTNETTRUNKDIR_WIRE))
2163 fRc = intnetR0NetworkSendUnicast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2164 else
2165 fRc = intnetR0NetworkSendUnicastWithSharedMac(pNetwork, pSG, fTrunkLocked, &EthHdr);
2166 return fRc;
2167}
2168
2169
2170/**
2171 * Sends one or more frames.
2172 *
2173 * The function will first the frame which is passed as the optional
2174 * arguments pvFrame and cbFrame. These are optional since it also
2175 * possible to chain together one or more frames in the send buffer
2176 * which the function will process after considering it's arguments.
2177 *
2178 * @returns VBox status code.
2179 * @param pIntNet The instance data.
2180 * @param hIf The interface handle.
2181 * @param pSession The caller's session.
2182 * @param pvFrame Pointer to the frame. Optional, please don't use.
2183 * @param cbFrame Size of the frame. Optional, please don't use.
2184 */
2185INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, const void *pvFrame, unsigned cbFrame)
2186{
2187 Log5(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
2188
2189 /*
2190 * Validate input and translate the handle.
2191 */
2192 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2193 if (pvFrame && cbFrame)
2194 {
2195 AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
2196 AssertPtrReturn(pvFrame, VERR_INVALID_PARAMETER);
2197 AssertPtrReturn((uint8_t *)pvFrame + cbFrame - 1, VERR_INVALID_PARAMETER);
2198
2199 /* This is the better place to crash, probe the buffer. */
2200 ASMProbeReadBuffer(pvFrame, cbFrame);
2201 }
2202 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2203 if (!pIf)
2204 return VERR_INVALID_HANDLE;
2205
2206 /*
2207 * Lock the network. If there is a trunk retain it and grab its
2208 * out-bound lock (this requires leaving the network lock first).
2209 * Grabbing the out-bound lock here simplifies things quite a bit
2210 * later on, so while this is excessive and a bit expensive it's
2211 * not worth caring about right now.
2212 */
2213 PINTNETNETWORK pNetwork = pIf->pNetwork;
2214 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2215 if (RT_FAILURE(rc))
2216 {
2217 intnetR0IfRelease(pIf, pSession);
2218 return rc;
2219 }
2220 PINTNETTRUNKIF pTrunkIf = intnetR0TrunkIfRetain(pNetwork->pTrunkIF);
2221 if (pTrunkIf)
2222 {
2223 RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2224
2225 if (!intnetR0TrunkIfOutLock(pTrunkIf))
2226 {
2227 intnetR0TrunkIfRelease(pTrunkIf);
2228 intnetR0IfRelease(pIf, pSession);
2229 return VERR_SEM_DESTROYED;
2230 }
2231
2232 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2233 if (RT_FAILURE(rc))
2234 {
2235 intnetR0TrunkIfOutUnlock(pTrunkIf);
2236 intnetR0TrunkIfRelease(pTrunkIf);
2237 intnetR0IfRelease(pIf, pSession);
2238 return rc;
2239 }
2240 }
2241
2242 INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
2243 * with buffer sharing for some OS or service. Darwin copies everything so
2244 * I won't bother allocating and managing SGs rigth now. Sorry. */
2245
2246 /*
2247 * Process the argument.
2248 */
2249 if (pvFrame && cbFrame)
2250 {
2251 intnetR0SgInitTemp(&Sg, (void *)pvFrame, cbFrame);
2252 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2253 }
2254
2255 /*
2256 * Process the send buffer.
2257 */
2258 while (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
2259 {
2260 /* Send the frame if the type is sane. */
2261 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
2262 if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
2263 {
2264 void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
2265 if (pvCurFrame)
2266 {
2267 intnetR0SgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
2268 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2269 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, (uint16_t *)&Sg.fFlags);
2270 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2271 }
2272 }
2273 /* else: ignore the frame */
2274
2275 /* Skip to the next frame. */
2276 INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
2277 }
2278
2279 /*
2280 * Release the semaphore(s) and release references.
2281 */
2282 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2283 if (pTrunkIf)
2284 {
2285 intnetR0TrunkIfOutUnlock(pTrunkIf);
2286 intnetR0TrunkIfRelease(pTrunkIf);
2287 }
2288
2289 intnetR0IfRelease(pIf, pSession);
2290 return rc;
2291}
2292
2293
2294/**
2295 * VMMR0 request wrapper for INTNETR0IfSend.
2296 *
2297 * @returns see INTNETR0IfSend.
2298 * @param pIntNet The internal networking instance.
2299 * @param pSession The caller's session.
2300 * @param pReq The request packet.
2301 */
2302INTNETR0DECL(int) INTNETR0IfSendReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
2303{
2304 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2305 return VERR_INVALID_PARAMETER;
2306 return INTNETR0IfSend(pIntNet, pReq->hIf, pSession, NULL, 0);
2307}
2308
2309
2310/**
2311 * Maps the default buffer into ring 3.
2312 *
2313 * @returns VBox status code.
2314 * @param pIntNet The instance data.
2315 * @param hIf The interface handle.
2316 * @param pSession The caller's session.
2317 * @param ppRing3Buf Where to store the address of the ring-3 mapping.
2318 */
2319INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
2320{
2321 LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
2322
2323 /*
2324 * Validate input.
2325 */
2326 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2327 AssertPtrReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
2328 *ppRing3Buf = 0;
2329 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2330 if (!pIf)
2331 return VERR_INVALID_HANDLE;
2332
2333 /*
2334 * ASSUMES that only the process that created an interface can use it.
2335 * ASSUMES that we created the ring-3 mapping when selecting or
2336 * allocating the buffer.
2337 */
2338 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2339 if (RT_SUCCESS(rc))
2340 {
2341 *ppRing3Buf = pIf->pIntBufR3;
2342 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2343 }
2344
2345 intnetR0IfRelease(pIf, pSession);
2346 LogFlow(("INTNETR0IfGetRing3Buffer: returns %Rrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
2347 return rc;
2348}
2349
2350
2351/**
2352 * VMMR0 request wrapper for INTNETR0IfGetRing3Buffer.
2353 *
2354 * @returns see INTNETR0IfGetRing3Buffer.
2355 * @param pIntNet The internal networking instance.
2356 * @param pSession The caller's session.
2357 * @param pReq The request packet.
2358 */
2359INTNETR0DECL(int) INTNETR0IfGetRing3BufferReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFGETRING3BUFFERREQ pReq)
2360{
2361 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2362 return VERR_INVALID_PARAMETER;
2363 return INTNETR0IfGetRing3Buffer(pIntNet, pReq->hIf, pSession, &pReq->pRing3Buf);
2364}
2365
2366
2367/**
2368 * Gets the ring-0 address of the current buffer.
2369 *
2370 * @returns VBox status code.
2371 * @param pIntNet The instance data.
2372 * @param hIf The interface handle.
2373 * @param pSession The caller's session.
2374 * @param ppRing0Buf Where to store the address of the ring-3 mapping.
2375 */
2376INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF *ppRing0Buf)
2377{
2378 LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
2379
2380 /*
2381 * Validate input.
2382 */
2383 AssertPtrReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
2384 *ppRing0Buf = NULL;
2385 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2386 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2387 if (!pIf)
2388 return VERR_INVALID_HANDLE;
2389
2390 /*
2391 * Grab the lock and get the data.
2392 * ASSUMES that the handle isn't closed while we're here.
2393 */
2394 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2395 if (RT_SUCCESS(rc))
2396 {
2397 *ppRing0Buf = pIf->pIntBuf;
2398
2399 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2400 }
2401 intnetR0IfRelease(pIf, pSession);
2402 LogFlow(("INTNETR0IfGetRing0Buffer: returns %Rrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
2403 return rc;
2404}
2405
2406
2407#if 0
2408/**
2409 * Gets the physical addresses of the default interface buffer.
2410 *
2411 * @returns VBox status code.
2412 * @param pIntNet The instance data.
2413 * @param hIF The interface handle.
2414 * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
2415 * @param cPages
2416 */
2417INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
2418{
2419 /*
2420 * Validate input.
2421 */
2422 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2423 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
2424 AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
2425 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2426 if (!pIf)
2427 return VERR_INVALID_HANDLE;
2428
2429 /*
2430 * Grab the lock and get the data.
2431 * ASSUMES that the handle isn't closed while we're here.
2432 */
2433 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2434 if (RT_SUCCESS(rc))
2435 {
2436 /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
2437 * is no need for any extra bookkeeping here.. */
2438
2439 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2440 }
2441 intnetR0IfRelease(pIf, pSession);
2442 return VERR_NOT_IMPLEMENTED;
2443}
2444#endif
2445
2446
2447/**
2448 * Sets the promiscuous mode property of an interface.
2449 *
2450 * @returns VBox status code.
2451 * @param pIntNet The instance handle.
2452 * @param hIf The interface handle.
2453 * @param pSession The caller's session.
2454 * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
2455 */
2456INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
2457{
2458 LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
2459
2460 /*
2461 * Validate & translate input.
2462 */
2463 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2464 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2465 if (!pIf)
2466 {
2467 Log(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
2468 return VERR_INVALID_HANDLE;
2469 }
2470
2471 /*
2472 * Grab the network semaphore and make the change.
2473 */
2474 int rc;
2475 PINTNETNETWORK pNetwork = pIf->pNetwork;
2476 if (pNetwork)
2477 {
2478 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2479 if (RT_SUCCESS(rc))
2480 {
2481 if (pIf->fPromiscuous != fPromiscuous)
2482 {
2483 Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
2484 hIf, !fPromiscuous, !!fPromiscuous));
2485 ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
2486 }
2487
2488 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2489 }
2490 }
2491 else
2492 rc = VERR_WRONG_ORDER;
2493
2494 intnetR0IfRelease(pIf, pSession);
2495 return rc;
2496}
2497
2498
2499/**
2500 * VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode.
2501 *
2502 * @returns see INTNETR0IfSetPromiscuousMode.
2503 * @param pIntNet The internal networking instance.
2504 * @param pSession The caller's session.
2505 * @param pReq The request packet.
2506 */
2507INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
2508{
2509 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2510 return VERR_INVALID_PARAMETER;
2511 return INTNETR0IfSetPromiscuousMode(pIntNet, pReq->hIf, pSession, pReq->fPromiscuous);
2512}
2513
2514
2515/**
2516 * Sets the MAC address of an interface.
2517 *
2518 * @returns VBox status code.
2519 * @param pIntNet The instance handle.
2520 * @param hIf The interface handle.
2521 * @param pSession The caller's session.
2522 * @param pMAC The new MAC address.
2523 */
2524INTNETR0DECL(int) INTNETR0IfSetMacAddress(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCPDMMAC pMac)
2525{
2526 LogFlow(("INTNETR0IfSetMacAddress: pIntNet=%p hIf=%RX32 pMac=%p:{%.6Rhxs}\n", pIntNet, hIf, pMac, pMac));
2527
2528 /*
2529 * Validate & translate input.
2530 */
2531 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2532 AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
2533 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2534 if (!pIf)
2535 {
2536 Log(("INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
2537 return VERR_INVALID_HANDLE;
2538 }
2539
2540 /*
2541 * Grab the network semaphore and make the change.
2542 */
2543 int rc;
2544 PINTNETNETWORK pNetwork = pIf->pNetwork;
2545 if (pNetwork)
2546 {
2547 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2548 if (RT_SUCCESS(rc))
2549 {
2550 if (memcmp(&pIf->Mac, pMac, sizeof(pIf->Mac)))
2551 {
2552 Log(("INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
2553 hIf, &pIf->Mac, pMac));
2554 pIf->Mac = *pMac;
2555 pIf->fMacSet = true;
2556 }
2557
2558 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2559 }
2560 }
2561 else
2562 rc = VERR_WRONG_ORDER;
2563
2564 intnetR0IfRelease(pIf, pSession);
2565 return rc;
2566}
2567
2568
2569/**
2570 * VMMR0 request wrapper for INTNETR0IfSetMacAddress.
2571 *
2572 * @returns see INTNETR0IfSetMacAddress.
2573 * @param pIntNet The internal networking instance.
2574 * @param pSession The caller's session.
2575 * @param pReq The request packet.
2576 */
2577INTNETR0DECL(int) INTNETR0IfSetMacAddressReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
2578{
2579 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2580 return VERR_INVALID_PARAMETER;
2581 return INTNETR0IfSetMacAddress(pIntNet, pReq->hIf, pSession, &pReq->Mac);
2582}
2583
2584
2585/**
2586 * Worker for intnetR0IfSetActive.
2587 *
2588 * This function will update the active interface count on the network and
2589 * activate or deactivate the trunk connection if necessary. Note that in
2590 * order to do this it is necessary to abandond the network semaphore.
2591 *
2592 * @returns VBox status code.
2593 * @param pNetwork The network.
2594 * @param fIf The interface.
2595 * @param fActive What to do.
2596 */
2597static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
2598{
2599 /* quick santiy check */
2600 AssertPtr(pNetwork);
2601 AssertPtr(pIf);
2602
2603 /*
2604 * If we've got a trunk, lock it now in case we need to call out, and
2605 * then lock the network.
2606 */
2607 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2608 if (pTrunkIf && !intnetR0TrunkIfOutLock(pTrunkIf))
2609 return VERR_SEM_DESTROYED;
2610
2611 int rc = RTSemFastMutexRequest(pNetwork->FastMutex); AssertRC(rc);
2612 if (RT_SUCCESS(rc))
2613 {
2614 bool fNetworkLocked = true;
2615
2616 /*
2617 * Make the change if necessary.
2618 */
2619 if (pIf->fActive != fActive)
2620 {
2621 pIf->fActive = fActive;
2622
2623 uint32_t const cActiveIFs = pNetwork->cActiveIFs;
2624 Assert((int32_t)cActiveIFs + (fActive ? 1 : -1) >= 0);
2625 pNetwork->cActiveIFs += fActive ? 1 : -1;
2626
2627 if ( pTrunkIf
2628 && ( !pNetwork->cActiveIFs
2629 || !cActiveIFs))
2630 {
2631 /*
2632 * We'll have to change the trunk status, so, leave
2633 * the network semaphore so we don't create any deadlocks.
2634 */
2635 int rc2 = RTSemFastMutexRelease(pNetwork->FastMutex); AssertRC(rc2);
2636 fNetworkLocked = false;
2637
2638 if (pTrunkIf->pIfPort)
2639 pTrunkIf->pIfPort->pfnSetActive(pTrunkIf->pIfPort, fActive);
2640 }
2641 }
2642
2643 if (fNetworkLocked)
2644 RTSemFastMutexRelease(pNetwork->FastMutex);
2645 }
2646 if (pTrunkIf)
2647 intnetR0TrunkIfOutUnlock(pTrunkIf);
2648 return rc;
2649}
2650
2651
2652/**
2653 * Activates or deactivates a interface.
2654 *
2655 * This is used to enable and disable the trunk connection on demans as well as
2656 * know when not to expect an interface to want to receive packets.
2657 *
2658 * @returns VBox status code.
2659 * @param pIf The interface.
2660 * @param fActive What to do.
2661 */
2662static int intnetR0IfSetActive(PINTNETIF pIf, bool fActive)
2663{
2664 /* quick sanity check */
2665 AssertPtrReturn(pIf, VERR_INVALID_POINTER);
2666
2667 /*
2668 * Hand it to the network since it might involve the trunk
2669 * and things are tricky there wrt to locking order.
2670 */
2671 PINTNETNETWORK pNetwork = pIf->pNetwork;
2672 if (!pNetwork)
2673 return VERR_WRONG_ORDER;
2674 return intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2675}
2676
2677
2678/**
2679 * Sets the active property of an interface.
2680 *
2681 * @returns VBox status code.
2682 * @param pIntNet The instance handle.
2683 * @param hIf The interface handle.
2684 * @param pSession The caller's session.
2685 * @param fActive The new state.
2686 */
2687INTNETR0DECL(int) INTNETR0IfSetActive(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
2688{
2689 LogFlow(("INTNETR0IfSetActive: pIntNet=%p hIf=%RX32 fActive=%RTbool\n", pIntNet, hIf, fActive));
2690
2691 /*
2692 * Validate & translate input.
2693 */
2694 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2695 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2696 if (!pIf)
2697 {
2698 Log(("INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
2699 return VERR_INVALID_HANDLE;
2700 }
2701
2702 /*
2703 * Hand it to the network since it might involve the trunk
2704 * and things are tricky there wrt to locking order.
2705 */
2706 int rc;
2707 PINTNETNETWORK pNetwork = pIf->pNetwork;
2708 if (pNetwork)
2709 rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2710 else
2711 rc = VERR_WRONG_ORDER;
2712
2713 intnetR0IfRelease(pIf, pSession);
2714 return rc;
2715}
2716
2717
2718/**
2719 * VMMR0 request wrapper for INTNETR0IfSetActive.
2720 *
2721 * @returns see INTNETR0IfSetActive.
2722 * @param pIntNet The internal networking instance.
2723 * @param pSession The caller's session.
2724 * @param pReq The request packet.
2725 */
2726INTNETR0DECL(int) INTNETR0IfSetActiveReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
2727{
2728 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2729 return VERR_INVALID_PARAMETER;
2730 return INTNETR0IfSetActive(pIntNet, pReq->hIf, pSession, pReq->fActive);
2731}
2732
2733
2734/**
2735 * Wait for the interface to get signaled.
2736 * The interface will be signaled when is put into the receive buffer.
2737 *
2738 * @returns VBox status code.
2739 * @param pIntNet The instance handle.
2740 * @param hIf The interface handle.
2741 * @param pSession The caller's session.
2742 * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
2743 * used if indefinite wait is desired.
2744 */
2745INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
2746{
2747 Log4(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));
2748
2749 /*
2750 * Get and validate essential handles.
2751 */
2752 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2753 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2754 if (!pIf)
2755 {
2756 Log(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
2757 return VERR_INVALID_HANDLE;
2758 }
2759 const INTNETIFHANDLE hIfSelf = pIf->hIf;
2760 const RTSEMEVENT Event = pIf->Event;
2761 if ( hIfSelf != hIf /* paranoia */
2762 && Event != NIL_RTSEMEVENT)
2763 {
2764 Log(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
2765 return VERR_SEM_DESTROYED;
2766 }
2767
2768 /*
2769 * It is tempting to check if there is data to be read here,
2770 * but the problem with such an approach is that it will cause
2771 * one unnecessary supervisor->user->supervisor trip. There is
2772 * already a slight risk for such, so no need to increase it.
2773 */
2774
2775 /*
2776 * Increment the number of waiters before starting the wait.
2777 * Upon wakeup we must assert reality, checking that we're not
2778 * already destroyed or in the process of being destroyed. This
2779 * code must be aligned with the waiting code in intnetR0IfDestruct.
2780 */
2781 ASMAtomicIncU32(&pIf->cSleepers);
2782 int rc = RTSemEventWaitNoResume(Event, cMillies);
2783 if (pIf->Event == Event)
2784 {
2785 ASMAtomicDecU32(&pIf->cSleepers);
2786 if (!pIf->fDestroying)
2787 {
2788 intnetR0IfRelease(pIf, pSession);
2789 if (pIf->hIf != hIf)
2790 rc = VERR_SEM_DESTROYED;
2791 }
2792 else
2793 rc = VERR_SEM_DESTROYED;
2794 }
2795 else
2796 rc = VERR_SEM_DESTROYED;
2797 Log4(("INTNETR0IfWait: returns %Rrc\n", rc));
2798 return rc;
2799}
2800
2801
2802/**
2803 * VMMR0 request wrapper for INTNETR0IfWait.
2804 *
2805 * @returns see INTNETR0IfWait.
2806 * @param pIntNet The internal networking instance.
2807 * @param pSession The caller's session.
2808 * @param pReq The request packet.
2809 */
2810INTNETR0DECL(int) INTNETR0IfWaitReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
2811{
2812 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2813 return VERR_INVALID_PARAMETER;
2814 return INTNETR0IfWait(pIntNet, pReq->hIf, pSession, pReq->cMillies);
2815}
2816
2817
2818/**
2819 * Close an interface.
2820 *
2821 * @returns VBox status code.
2822 * @param pIntNet The instance handle.
2823 * @param hIf The interface handle.
2824 * @param pSession The caller's session.
2825 */
2826INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
2827{
2828 LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));
2829
2830 /*
2831 * Validate and free the handle.
2832 */
2833 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2834 PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
2835 if (!pIf)
2836 return VERR_INVALID_HANDLE;
2837
2838 /* mark the handle as freed so intnetR0IfDestruct won't free it again. */
2839 ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
2840
2841
2842 /*
2843 * Release the references to the interface object (handle + free lookup).
2844 * But signal the event semaphore first so any waiter holding a reference
2845 * will wake up too (he'll see hIf == invalid and return correctly).
2846 */
2847 RTSemEventSignal(pIf->Event);
2848
2849 void *pvObj = pIf->pvObj;
2850 intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
2851
2852 int rc = SUPR0ObjRelease(pvObj, pSession);
2853 LogFlow(("INTNETR0IfClose: returns %Rrc\n", rc));
2854 return rc;
2855}
2856
2857
2858/**
2859 * VMMR0 request wrapper for INTNETR0IfCloseReq.
2860 *
2861 * @returns see INTNETR0IfClose.
2862 * @param pIntNet The internal networking instance.
2863 * @param pSession The caller's session.
2864 * @param pReq The request packet.
2865 */
2866INTNETR0DECL(int) INTNETR0IfCloseReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
2867{
2868 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2869 return VERR_INVALID_PARAMETER;
2870 return INTNETR0IfClose(pIntNet, pReq->hIf, pSession);
2871}
2872
2873
2874/**
2875 * Interface destructor callback.
2876 * This is called for reference counted objectes when the count reaches 0.
2877 *
2878 * @param pvObj The object pointer.
2879 * @param pvUser1 Pointer to the interface.
2880 * @param pvUser2 Pointer to the INTNET instance data.
2881 */
2882static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
2883{
2884 PINTNETIF pIf = (PINTNETIF)pvUser1;
2885 PINTNET pIntNet = (PINTNET)pvUser2;
2886 Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
2887
2888 RTSemFastMutexRequest(pIntNet->FastMutex);
2889
2890 /*
2891 * Mark the interface as being destroyed so the waiter
2892 * can behave appropriately (theoretical case).
2893 */
2894 ASMAtomicWriteBool(&pIf->fDestroying, true);
2895
2896 /*
2897 * Delete the interface handle so the object no longer can be used.
2898 * (Can happen if the client didn't close its session.)
2899 */
2900 INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
2901 if (hIf != INTNET_HANDLE_INVALID)
2902 {
2903 void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession);
2904 AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
2905 }
2906
2907 /*
2908 * If we've got a network deactivate and unlink ourselves from it.
2909 * Because of cleanup order we might be an orphan now.
2910 */
2911 PINTNETNETWORK pNetwork = pIf->pNetwork;
2912 if (pNetwork)
2913 {
2914 intnetR0IfSetActive(pIf, false);
2915
2916 if (pNetwork->pIFs == pIf)
2917 pNetwork->pIFs = pIf->pNext;
2918 else
2919 {
2920 PINTNETIF pPrev = pNetwork->pIFs;
2921 while (pPrev)
2922 {
2923 if (pPrev->pNext == pIf)
2924 {
2925 pPrev->pNext = pIf->pNext;
2926 break;
2927 }
2928 pPrev = pPrev->pNext;
2929 }
2930 Assert(pPrev);
2931 }
2932 pIf->pNext = NULL;
2933
2934 /*
2935 * Release our reference to the network.
2936 */
2937 RTSemFastMutexRelease(pIntNet->FastMutex);
2938
2939 SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
2940 pIf->pNetwork = NULL;
2941 }
2942 else
2943 RTSemFastMutexRelease(pIntNet->FastMutex);
2944
2945 /*
2946 * Wakeup anyone waiting on this interface.
2947 *
2948 * We *must* make sure they have woken up properly and realized
2949 * that the interface is no longer valid.
2950 */
2951 if (pIf->Event != NIL_RTSEMEVENT)
2952 {
2953 RTSEMEVENT Event = pIf->Event;
2954 unsigned cMaxWait = 0x1000;
2955 while (pIf->cSleepers && cMaxWait-- > 0)
2956 {
2957 RTSemEventSignal(Event);
2958 RTThreadYield();
2959 }
2960 if (pIf->cSleepers)
2961 {
2962 RTThreadSleep(1);
2963
2964 cMaxWait = pIf->cSleepers;
2965 while (pIf->cSleepers && cMaxWait-- > 0)
2966 {
2967 RTSemEventSignal(Event);
2968 RTThreadSleep(10);
2969 }
2970 }
2971
2972 RTSemEventDestroy(Event);
2973 pIf->Event = NIL_RTSEMEVENT;
2974 }
2975
2976 /*
2977 * Unmap user buffer.
2978 */
2979 if (pIf->pIntBuf != pIf->pIntBufDefault)
2980 {
2981 /** @todo user buffer */
2982 }
2983
2984 /*
2985 * Unmap and Free the default buffer.
2986 */
2987 if (pIf->pIntBufDefault)
2988 {
2989 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
2990 pIf->pIntBufDefault = NULL;
2991 pIf->pIntBufDefaultR3 = 0;
2992 pIf->pIntBuf = NULL;
2993 pIf->pIntBufR3 = 0;
2994 }
2995
2996 /*
2997 * The interface.
2998 */
2999 pIf->pvObj = NULL;
3000 RTMemFree(pIf);
3001}
3002
3003
3004/**
3005 * Creates a new network interface.
3006 *
3007 * The call must have opened the network for the new interface
3008 * and is responsible for closing it on failure. On success
3009 * it must leave the network opened so the interface destructor
3010 * can close it.
3011 *
3012 * @returns VBox status code.
3013 * @param pNetwork The network.
3014 * @param pSession The session handle.
3015 * @param cbSend The size of the send buffer.
3016 * @param cbRecv The size of the receive buffer.
3017 * @param phIf Where to store the interface handle.
3018 */
3019static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, bool *pfCloseNetwork, PINTNETIFHANDLE phIf)
3020{
3021 LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
3022 pNetwork, pSession, cbSend, cbRecv, phIf));
3023
3024 /*
3025 * Assert input.
3026 */
3027 AssertPtr(pNetwork);
3028 AssertPtr(phIf);
3029 AssertPtr(pfCloseNetwork);
3030 *pfCloseNetwork = false;
3031
3032 /*
3033 * Allocate and initialize the interface structure.
3034 */
3035 PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
3036 if (!pIf)
3037 return VERR_NO_MEMORY;
3038 //pIf->pNext = NULL;
3039 memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
3040 //pIf->fMacSet = false;
3041 //pIf->fPromiscuous = false;
3042 //pIf->fActive = false;
3043 //pIf->fDestroying = false;
3044 //pIf->pIntBuf = 0;
3045 //pIf->pIntBufR3 = NIL_RTR3PTR;
3046 //pIf->pIntBufDefault = 0;
3047 //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
3048 //pIf->cYields = 0;
3049 pIf->Event = NIL_RTSEMEVENT;
3050 //pIf->cSleepers = 0;
3051 pIf->hIf = INTNET_HANDLE_INVALID;
3052 pIf->pNetwork = pNetwork;
3053 pIf->pSession = pSession;
3054 //pIf->pvObj = NULL;
3055 //pIf->aAddrCache[kIntNetAddrType_Invalid] = {0};
3056 //pIf->aAddrCache[kIntNetAddrType_IPv4].pbEntries = NULL;
3057 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntries = 0;
3058 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntriesAlloc = 0;
3059 pIf->aAddrCache[kIntNetAddrType_IPv4].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv4);
3060 pIf->aAddrCache[kIntNetAddrType_IPv4].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv4);
3061 //pIf->aAddrCache[kIntNetAddrType_IPv6].pbEntries = NULL;
3062 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntries = 0;
3063 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntriesAlloc = 0;
3064 pIf->aAddrCache[kIntNetAddrType_IPv6].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv6);
3065 pIf->aAddrCache[kIntNetAddrType_IPv6].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv6);
3066 //pIf->aAddrCache[kIntNetAddrType_IPX].pbEntries = NULL;
3067 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntries = 0;
3068 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntriesAlloc = 0;
3069 pIf->aAddrCache[kIntNetAddrType_IPX].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPX);
3070 pIf->aAddrCache[kIntNetAddrType_IPX].cbEntry = RT_ALIGN_32(intnetR0AddrSize(kIntNetAddrType_IPv4), 16);
3071 int rc = RTSemEventCreate((PRTSEMEVENT)&pIf->Event);
3072 if (RT_SUCCESS(rc))
3073 {
3074 /*
3075 * Create the default buffer.
3076 */
3077 /** @todo adjust with minimums and apply defaults here. */
3078 cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3079 cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3080 const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
3081 rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
3082 if (RT_SUCCESS(rc))
3083 {
3084 ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
3085
3086 pIf->pIntBuf = pIf->pIntBufDefault;
3087 pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
3088 pIf->pIntBuf->cbBuf = cbBuf;
3089 pIf->pIntBuf->cbRecv = cbRecv;
3090 pIf->pIntBuf->cbSend = cbSend;
3091 /* receive ring buffer. */
3092 pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
3093 pIf->pIntBuf->Recv.offRead = pIf->pIntBuf->Recv.offStart;
3094 pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
3095 pIf->pIntBuf->Recv.offEnd = pIf->pIntBuf->Recv.offStart + cbRecv;
3096 /* send ring buffer. */
3097 pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
3098 pIf->pIntBuf->Send.offRead = pIf->pIntBuf->Send.offStart;
3099 pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
3100 pIf->pIntBuf->Send.offEnd = pIf->pIntBuf->Send.offStart + cbSend;
3101
3102 /*
3103 * Link the interface to the network.
3104 */
3105 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3106 if (RT_SUCCESS(rc))
3107 {
3108 pIf->pNext = pNetwork->pIFs;
3109 pNetwork->pIFs = pIf;
3110 RTSemFastMutexRelease(pNetwork->FastMutex);
3111
3112 /*
3113 * Register the interface with the session.
3114 */
3115 pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, intnetR0IfDestruct, pIf, pNetwork->pIntNet);
3116 if (pIf->pvObj)
3117 {
3118 rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
3119 if (RT_SUCCESS(rc))
3120 {
3121 *phIf = pIf->hIf;
3122 Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
3123 *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
3124 return VINF_SUCCESS;
3125 }
3126
3127 SUPR0ObjRelease(pIf->pvObj, pSession);
3128 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3129 return rc;
3130 }
3131
3132 RTSemFastMutexDestroy(pNetwork->FastMutex);
3133 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3134 }
3135
3136 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
3137 pIf->pIntBufDefault = NULL;
3138 pIf->pIntBuf = NULL;
3139 }
3140
3141 RTSemEventDestroy(pIf->Event);
3142 pIf->Event = NIL_RTSEMEVENT;
3143 }
3144 RTMemFree(pIf);
3145 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3146 *pfCloseNetwork = true;
3147 return rc;
3148}
3149
3150
3151
3152
3153
3154/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
3155static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
3156{
3157 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3158 AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
3159 return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
3160}
3161
3162
3163/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
3164static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
3165{
3166 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3167 PINTNETNETWORK pNetwork = pThis->pNetwork;
3168
3169 /* assert some sanity */
3170 AssertPtrReturn(pNetwork, false);
3171 AssertReturn(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX, false);
3172 AssertPtr(pSG);
3173 Assert(fSrc);
3174
3175 /*
3176 * Lock the network and send the frame to it.
3177 */
3178 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3179 AssertRCReturn(rc, false);
3180
3181 bool fRc;
3182 if (RT_LIKELY(pNetwork->cActiveIFs > 0))
3183 fRc = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, false /* fTrunkLocked */);
3184 else
3185 fRc = false; /* don't drop it */
3186
3187 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
3188 AssertRC(rc);
3189
3190 return fRc;
3191}
3192
3193
3194/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
3195static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3196{
3197 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3198 PINTNETNETWORK pNetwork = pThis->pNetwork;
3199
3200 /* assert some sanity */
3201 AssertPtrReturnVoid(pNetwork);
3202 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3203 AssertPtr(pSG);
3204 Assert(pSG->cUsers > 0);
3205
3206 /* do it. */
3207 ++pSG->cUsers;
3208}
3209
3210
3211/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
3212static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3213{
3214 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3215 PINTNETNETWORK pNetwork = pThis->pNetwork;
3216
3217 /* assert some sanity */
3218 AssertPtrReturnVoid(pNetwork);
3219 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3220 AssertPtr(pSG);
3221 Assert(pSG->cUsers > 0);
3222
3223 /*
3224 * Free it?
3225 */
3226 if (!--pSG->cUsers)
3227 {
3228 /** @todo later */
3229 }
3230}
3231
3232
3233/**
3234 * Retain the trunk interface.
3235 *
3236 * @returns pThis if retained.
3237 *
3238 * @param pThis The trunk.
3239 *
3240 * @remarks Any locks.
3241 */
3242static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
3243{
3244 if (pThis && pThis->pIfPort)
3245 {
3246 pThis->pIfPort->pfnRetain(pThis->pIfPort);
3247 return pThis;
3248 }
3249 return NULL;
3250}
3251
3252
3253/**
3254 * Release the trunk interface.
3255 *
3256 * @param pThis The trunk.
3257 */
3258static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
3259{
3260 if (pThis && pThis->pIfPort)
3261 pThis->pIfPort->pfnRelease(pThis->pIfPort);
3262}
3263
3264
3265/**
3266 * Takes the out-bound trunk lock.
3267 *
3268 * This will ensure that pIfPort is valid.
3269 *
3270 * @returns success indicator.
3271 * @param pThis The trunk.
3272 *
3273 * @remarks No locks other than the create/destroy one.
3274 */
3275static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis)
3276{
3277 AssertPtrReturn(pThis, false);
3278 int rc = RTSemFastMutexRequest(pThis->FastMutex);
3279 if (RT_SUCCESS(rc))
3280 {
3281 if (RT_LIKELY(pThis->pIfPort))
3282 return true;
3283 RTSemFastMutexRelease(pThis->FastMutex);
3284 }
3285 else
3286 AssertMsg(rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc));
3287 return false;
3288}
3289
3290
3291/**
3292 * Releases the out-bound trunk lock.
3293 *
3294 * @param pThis The trunk.
3295 */
3296static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis)
3297{
3298 if (pThis)
3299 {
3300 int rc = RTSemFastMutexRelease(pThis->FastMutex);
3301 AssertRC(rc);
3302 }
3303}
3304
3305
3306/**
3307 * Activates the trunk interface.
3308 *
3309 * @param pThis The trunk.
3310 * @param fActive What to do with it.
3311 *
3312 * @remarks Caller may only own the create/destroy lock.
3313 */
3314static void intnetR0TrunkIfActivate(PINTNETTRUNKIF pThis, bool fActive)
3315{
3316 if (intnetR0TrunkIfOutLock(pThis))
3317 {
3318 pThis->pIfPort->pfnSetActive(pThis->pIfPort, fActive);
3319 intnetR0TrunkIfOutUnlock(pThis);
3320 }
3321}
3322
3323
3324/**
3325 * Shutdown the trunk interface.
3326 *
3327 * @param pThis The trunk.
3328 * @param pNetworks The network.
3329 *
3330 * @remarks The caller must *NOT* hold the network lock. The global
3331 * create/destroy lock is fine though.
3332 */
3333static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
3334{
3335 /* assert sanity */
3336 if (!pThis)
3337 return;
3338 AssertPtr(pThis);
3339 Assert(pThis->pNetwork == pNetwork);
3340 AssertPtrNull(pThis->pIfPort);
3341
3342 /*
3343 * The interface has already been deactivated, we just to wait for
3344 * it to become idle before we can disconnect and release it.
3345 */
3346 PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
3347 if (pIfPort)
3348 {
3349 intnetR0TrunkIfOutLock(pThis);
3350
3351 /* unset it */
3352 pThis->pIfPort = NULL;
3353
3354 /* wait in portions so we can complain ever now an then. */
3355 uint64_t StartTS = RTTimeSystemNanoTS();
3356 int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3357 if (RT_FAILURE(rc))
3358 {
3359 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3360 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3361 Assert(rc == VERR_TIMEOUT);
3362 while ( RT_FAILURE(rc)
3363 && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
3364 rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3365 if (rc == VERR_TIMEOUT)
3366 {
3367 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3368 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3369 while ( rc == VERR_TIMEOUT
3370 && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
3371 rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
3372 if (RT_FAILURE(rc))
3373 {
3374 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
3375 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3376 AssertRC(rc);
3377 }
3378 }
3379 }
3380
3381 /* disconnect & release it. */
3382 pIfPort->pfnDisconnectAndRelease(pIfPort);
3383 }
3384
3385 /*
3386 * Free up the resources.
3387 */
3388 RTSEMFASTMUTEX FastMutex = pThis->FastMutex;
3389 pThis->FastMutex = NIL_RTSEMFASTMUTEX;
3390 pThis->pNetwork = NULL;
3391 RTSemFastMutexRelease(FastMutex);
3392 RTSemFastMutexDestroy(FastMutex);
3393 RTMemFree(pThis);
3394}
3395
3396
3397/**
3398 * Creates the trunk connection (if any).
3399 *
3400 * @returns VBox status code.
3401 *
3402 * @param pNetwork The newly created network.
3403 * @param pSession The session handle.
3404 */
3405static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3406{
3407 const char *pszName;
3408 switch (pNetwork->enmTrunkType)
3409 {
3410 /*
3411 * The 'None' case, simple.
3412 */
3413 case kIntNetTrunkType_None:
3414 case kIntNetTrunkType_WhateverNone:
3415 return VINF_SUCCESS;
3416
3417 /* Can't happen, but makes GCC happy. */
3418 default:
3419 return VERR_NOT_IMPLEMENTED;
3420
3421 /*
3422 * Translate enum to component factory name.
3423 */
3424 case kIntNetTrunkType_NetFlt:
3425 pszName = "VBoxNetFlt";
3426 break;
3427 case kIntNetTrunkType_NetTap:
3428 pszName = "VBoxNetTap";
3429 break;
3430 case kIntNetTrunkType_SrvNat:
3431 pszName = "VBoxSrvNat";
3432 break;
3433 }
3434
3435 /*
3436 * Allocate the trunk interface.
3437 */
3438 PINTNETTRUNKIF pTrunkIF = (PINTNETTRUNKIF)RTMemAllocZ(sizeof(*pTrunkIF));
3439 if (!pTrunkIF)
3440 return VERR_NO_MEMORY;
3441 pTrunkIF->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
3442 pTrunkIF->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
3443 pTrunkIF->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
3444 pTrunkIF->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
3445 pTrunkIF->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
3446 pTrunkIF->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
3447 //pTrunkIF->pIfPort = NULL;
3448 pTrunkIF->pNetwork = pNetwork;
3449 //pTrunkIF->fPhysSG = false;
3450 //pTrunkIF->fPromiscuousWire = false;
3451 pTrunkIF->CachedMac.au8[0] = 0xfe;
3452 pTrunkIF->CachedMac.au8[1] = 0xff;
3453 pTrunkIF->CachedMac.au8[2] = 0xff;
3454 pTrunkIF->CachedMac.au8[3] = 0xff;
3455 pTrunkIF->CachedMac.au8[4] = 0xff;
3456 pTrunkIF->CachedMac.au8[5] = 0xff;
3457 int rc = RTSemFastMutexCreate(&pTrunkIF->FastMutex);
3458 if (RT_SUCCESS(rc))
3459 {
3460#ifdef IN_RING0 /* (testcase is ring-3) */
3461 /*
3462 * Query the factory we want, then use it create and connect the trunk.
3463 */
3464 PINTNETTRUNKFACTORY pTrunkFactory = NULL;
3465 rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
3466 if (RT_SUCCESS(rc))
3467 {
3468 rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory, pNetwork->szTrunk, &pTrunkIF->SwitchPort, &pTrunkIF->pIfPort);
3469 pTrunkFactory->pfnRelease(pTrunkFactory);
3470 if (RT_SUCCESS(rc))
3471 {
3472 Assert(pTrunkIF->pIfPort);
3473 pNetwork->pTrunkIF = pTrunkIF;
3474 LogFlow(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s Network=%s\n",
3475 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
3476 return VINF_SUCCESS;
3477 }
3478 }
3479#endif /* IN_RING0 */
3480 RTSemFastMutexDestroy(pTrunkIF->FastMutex);
3481 }
3482 RTMemFree(pTrunkIF);
3483 LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
3484 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
3485 return rc;
3486}
3487
3488
3489
3490/**
3491 * Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork().
3492 *
3493 * @param pNetwork The network to close.
3494 * @param pSession The session handle.
3495 */
3496static int intnetR0NetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3497{
3498 LogFlow(("intnetR0NetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
3499 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
3500 AssertPtrReturn(pNetwork, VERR_INVALID_PARAMETER);
3501
3502 int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
3503 LogFlow(("intnetR0NetworkClose: return %Rrc\n", rc));
3504 return rc;
3505}
3506
3507
3508/**
3509 * Object destructor callback.
3510 * This is called for reference counted objectes when the count reaches 0.
3511 *
3512 * @param pvObj The object pointer.
3513 * @param pvUser1 Pointer to the network.
3514 * @param pvUser2 Pointer to the INTNET instance data.
3515 */
3516static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3517{
3518 PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
3519 PINTNET pIntNet = (PINTNET)pvUser2;
3520 Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
3521 Assert(pNetwork->pIntNet == pIntNet);
3522
3523 /* take the create/destroy sem. */
3524 RTSemFastMutexRequest(pIntNet->FastMutex);
3525
3526 /*
3527 * Deactivate the trunk connection first (if any).
3528 */
3529 if (pNetwork->pTrunkIF)
3530 intnetR0TrunkIfActivate(pNetwork->pTrunkIF, false /* fActive */);
3531
3532 /*
3533 * Unlink the network.
3534 * Note that it needn't be in the list if we failed during creation.
3535 */
3536 PINTNETNETWORK pPrev = pIntNet->pNetworks;
3537 if (pPrev == pNetwork)
3538 pIntNet->pNetworks = pNetwork->pNext;
3539 else
3540 {
3541 for (; pPrev; pPrev = pPrev->pNext)
3542 if (pPrev->pNext == pNetwork)
3543 {
3544 pPrev->pNext = pNetwork->pNext;
3545 break;
3546 }
3547 }
3548 pNetwork->pNext = NULL;
3549 pNetwork->pvObj = NULL;
3550
3551 /*
3552 * Because of the undefined order of the per session object dereferencing when closing a session,
3553 * we have to handle the case where the network is destroyed before the interfaces. We'll
3554 * deal with this by simply orphaning the interfaces.
3555 */
3556 RTSemFastMutexRequest(pNetwork->FastMutex);
3557
3558 PINTNETIF pCur = pNetwork->pIFs;
3559 while (pCur)
3560 {
3561 PINTNETIF pNext = pCur->pNext;
3562 pCur->pNext = NULL;
3563 pCur->pNetwork = NULL;
3564 pCur = pNext;
3565 }
3566
3567 /* Grab and zap the trunk pointer before leaving the mutex. */
3568 PINTNETTRUNKIF pTrunkIF = pNetwork->pTrunkIF;
3569 pNetwork->pTrunkIF = NULL;
3570
3571 RTSemFastMutexRelease(pNetwork->FastMutex);
3572
3573 /*
3574 * If there is a trunk, delete it.
3575 * Note that this may tak a while if we're unlucky...
3576 */
3577 if (pTrunkIF)
3578 intnetR0TrunkIfDestroy(pTrunkIF, pNetwork);
3579
3580 /*
3581 * Free resources.
3582 */
3583 RTSemFastMutexDestroy(pNetwork->FastMutex);
3584 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3585 RTMemFree(pNetwork);
3586
3587 /* release the create/destroy sem. (can be done before trunk destruction.) */
3588 RTSemFastMutexRelease(pIntNet->FastMutex);
3589}
3590
3591
3592/**
3593 * Opens an existing network.
3594 *
3595 * @returns VBox status code.
3596 * @param pIntNet The instance data.
3597 * @param pSession The current session.
3598 * @param pszNetwork The network name. This has a valid length.
3599 * @param enmTrunkType The trunk type.
3600 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3601 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3602 * @param ppNetwork Where to store the pointer to the network on success.
3603 */
3604static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3605 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3606{
3607 LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3608 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3609
3610 /* just pro forma validation, the caller is internal. */
3611 AssertPtr(pIntNet);
3612 AssertPtr(pSession);
3613 AssertPtr(pszNetwork);
3614 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3615 AssertPtr(pszTrunk);
3616 Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
3617 AssertPtr(ppNetwork);
3618 *ppNetwork = NULL;
3619
3620 /*
3621 * Search networks by name.
3622 */
3623 PINTNETNETWORK pCur;
3624 uint8_t cchName = strlen(pszNetwork);
3625 Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
3626
3627 pCur = pIntNet->pNetworks;
3628 while (pCur)
3629 {
3630 if ( pCur->cchName == cchName
3631 && !memcmp(pCur->szName, pszNetwork, cchName))
3632 {
3633 /*
3634 * Found the network, now check that we have the same ideas
3635 * about the trunk setup and security.
3636 */
3637 int rc;
3638 if ( enmTrunkType == kIntNetTrunkType_WhateverNone
3639 || ( pCur->enmTrunkType == enmTrunkType
3640 && !strcmp(pCur->szTrunk, pszTrunk)))
3641 {
3642 if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
3643 {
3644
3645 /*
3646 * Increment the reference and check that the session
3647 * can access this network.
3648 */
3649 rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
3650 if (RT_SUCCESS(rc))
3651 {
3652 if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
3653 rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
3654 if (RT_SUCCESS(rc))
3655 {
3656 pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
3657
3658 *ppNetwork = pCur;
3659 }
3660 else
3661 SUPR0ObjRelease(pCur->pvObj, pSession);
3662 }
3663 else if (rc == VERR_WRONG_ORDER)
3664 rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
3665 }
3666 else
3667 rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
3668 }
3669 else
3670 rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
3671
3672 LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
3673 return rc;
3674 }
3675 pCur = pCur->pNext;
3676 }
3677
3678 LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
3679 return VERR_NOT_FOUND;
3680}
3681
3682
3683/**
3684 * Creates a new network.
3685 *
3686 * The call must own the INTNET::FastMutex and has already attempted
3687 * opening the network and found it to be non-existing.
3688 *
3689 * @returns VBox status code.
3690 * @param pIntNet The instance data.
3691 * @param pSession The session handle.
3692 * @param pszNetwork The name of the network. This must be at least one character long and no longer
3693 * than the INTNETNETWORK::szName.
3694 * @param enmTrunkType The trunk type.
3695 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3696 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3697 * @param ppNetwork Where to store the network. In the case of failure whatever is returned
3698 * here should be dereferenced outside the INTNET::FastMutex.
3699 */
3700static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3701 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3702{
3703 LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3704 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3705
3706 /* just pro forma validation, the caller is internal. */
3707 AssertPtr(pIntNet);
3708 AssertPtr(pSession);
3709 AssertPtr(pszNetwork);
3710 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3711 AssertPtr(pszTrunk);
3712 Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
3713 AssertPtr(ppNetwork);
3714 *ppNetwork = NULL;
3715
3716 /*
3717 * Allocate and initialize.
3718 */
3719 size_t cb = sizeof(INTNETNETWORK);
3720 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3721 cb += INTNETNETWORK_TMP_SIZE + 64;
3722 PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(cb);
3723 if (!pNew)
3724 return VERR_NO_MEMORY;
3725 int rc = RTSemFastMutexCreate(&pNew->FastMutex);
3726 if (RT_SUCCESS(rc))
3727 {
3728 //pNew->pIFs = NULL;
3729 pNew->pIntNet = pIntNet;
3730 //pNew->cActiveIFs = 0;
3731 pNew->fFlags = fFlags;
3732 size_t cchName = strlen(pszNetwork);
3733 pNew->cchName = cchName;
3734 Assert(cchName && cchName < sizeof(pNew->szName)); /* caller's responsibility. */
3735 memcpy(pNew->szName, pszNetwork, cchName); /* '\0' by alloc. */
3736 pNew->enmTrunkType = enmTrunkType;
3737 Assert(strlen(pszTrunk) < sizeof(pNew->szTrunk)); /* caller's responsibility. */
3738 strcpy(pNew->szTrunk, pszTrunk);
3739 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3740 pNew->pbTmp = RT_ALIGN_PT(pNew + 1, 64, uint8_t *);
3741 //else
3742 // pNew->pbTmp = NULL;
3743
3744 /*
3745 * Register the object in the current session and link it into the network list.
3746 */
3747 pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNew, pIntNet);
3748 if (pNew->pvObj)
3749 {
3750 pNew->pNext = pIntNet->pNetworks;
3751 pIntNet->pNetworks = pNew;
3752
3753 /*
3754 * Check if the current session is actually allowed to create and open
3755 * the network. It is possible to implement network name based policies
3756 * and these must be checked now. SUPR0ObjRegister does no such checks.
3757 */
3758 rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
3759 if (RT_SUCCESS(rc))
3760 {
3761 /*
3762 * Connect the trunk.
3763 */
3764 rc = intnetR0NetworkCreateTrunkIf(pNew, pSession);
3765 if (RT_SUCCESS(rc))
3766 {
3767 *ppNetwork = pNew;
3768 LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
3769 return VINF_SUCCESS;
3770 }
3771 }
3772
3773 /*
3774 * We unlink it here so it cannot be opened when the caller leaves
3775 * INTNET::FastMutex before dereferencing it.
3776 */
3777 Assert(pIntNet->pNetworks == pNew);
3778 pIntNet->pNetworks = pNew->pNext;
3779 pNew->pNext = NULL;
3780
3781 *ppNetwork = pNew;
3782 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3783 return rc;
3784 }
3785 rc = VERR_NO_MEMORY;
3786
3787 RTSemFastMutexDestroy(pNew->FastMutex);
3788 pNew->FastMutex = NIL_RTSEMFASTMUTEX;
3789 }
3790 RTMemFree(pNew);
3791 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3792 return rc;
3793}
3794
3795
3796/**
3797 * Opens a network interface and connects it to the specified network.
3798 *
3799 * @returns VBox status code.
3800 * @param pIntNet The internal network instance.
3801 * @param pSession The session handle.
3802 * @param pszNetwork The network name.
3803 * @param enmTrunkType The trunk type.
3804 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3805 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3806 * @param fRestrictAccess Whether new participants should be subjected to access check or not.
3807 * @param cbSend The send buffer size.
3808 * @param cbRecv The receive buffer size.
3809 * @param phIf Where to store the handle to the network interface.
3810 */
3811INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork,
3812 INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
3813 unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
3814{
3815 LogFlow(("INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
3816 pIntNet, pSession, pszNetwork, pszNetwork, pszTrunk, pszTrunk, enmTrunkType, fFlags, cbSend, cbRecv, phIf));
3817
3818 /*
3819 * Validate input.
3820 */
3821 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3822
3823 AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
3824 const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
3825 AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
3826 size_t cchNetwork = pszNetworkEnd - pszNetwork;
3827 AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
3828
3829 if (pszTrunk)
3830 {
3831 AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
3832 const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
3833 AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
3834 }
3835 else
3836 pszTrunk = "";
3837
3838 AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
3839 ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
3840 switch (enmTrunkType)
3841 {
3842 case kIntNetTrunkType_None:
3843 case kIntNetTrunkType_WhateverNone:
3844 AssertReturn(!*pszTrunk, VERR_INVALID_PARAMETER);
3845 break;
3846
3847 case kIntNetTrunkType_NetFlt:
3848 AssertReturn(pszTrunk, VERR_INVALID_PARAMETER);
3849 break;
3850
3851 default:
3852 return VERR_NOT_IMPLEMENTED;
3853 }
3854
3855 AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
3856 AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
3857
3858 /*
3859 * Acquire the mutex to serialize open/create.
3860 */
3861 int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
3862 if (RT_FAILURE(rc))
3863 return rc;
3864
3865 /*
3866 * Try open / create the network and create an interface on it for the caller to use.
3867 *
3868 * Note that because of the destructors grabbing INTNET::FastMutex and us being required
3869 * to own this semaphore for the entire network opening / creation and interface creation
3870 * sequence, intnetR0CreateNetwork will have to defer the network cleanup to us on failure.
3871 */
3872 PINTNETNETWORK pNetwork = NULL;
3873 rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
3874 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
3875 {
3876 bool fCloseNetwork = true;
3877 if (rc == VERR_NOT_FOUND)
3878 rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
3879 if (RT_SUCCESS(rc))
3880 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, &fCloseNetwork, phIf);
3881
3882 RTSemFastMutexRelease(pIntNet->FastMutex);
3883
3884 if (RT_FAILURE(rc) && pNetwork && fCloseNetwork)
3885 intnetR0NetworkClose(pNetwork, pSession);
3886 }
3887 else
3888 RTSemFastMutexRelease(pIntNet->FastMutex);
3889
3890 LogFlow(("INTNETR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
3891 return rc;
3892}
3893
3894
3895/**
3896 * VMMR0 request wrapper for GMMR0MapUnmapChunk.
3897 *
3898 * @returns see GMMR0MapUnmapChunk.
3899 * @param pIntNet The internal networking instance.
3900 * @param pSession The caller's session.
3901 * @param pReq The request packet.
3902 */
3903INTNETR0DECL(int) INTNETR0OpenReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
3904{
3905 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
3906 return VERR_INVALID_PARAMETER;
3907 return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
3908 pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
3909}
3910
3911
3912/**
3913 * Destroys an instance of the Ring-0 internal networking service.
3914 *
3915 * @param pIntNet Pointer to the instance data.
3916 */
3917INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
3918{
3919 LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));
3920
3921 /*
3922 * Allow NULL pointers.
3923 */
3924 if (!pIntNet)
3925 return;
3926 AssertPtrReturnVoid(pIntNet);
3927
3928 /*
3929 * There is not supposed to be any networks hanging around at this time.
3930 */
3931 Assert(pIntNet->pNetworks == NULL);
3932 if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
3933 {
3934 RTSemFastMutexDestroy(pIntNet->FastMutex);
3935 pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
3936 }
3937 if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
3938 {
3939 /** @todo does it make sense to have a deleter here? */
3940 RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
3941 pIntNet->hHtIfs = NIL_RTHANDLETABLE;
3942 }
3943
3944 RTMemFree(pIntNet);
3945}
3946
3947
3948/**
3949 * Create an instance of the Ring-0 internal networking service.
3950 *
3951 * @returns VBox status code.
3952 * @param ppIntNet Where to store the instance pointer.
3953 */
3954INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
3955{
3956 LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
3957 int rc = VERR_NO_MEMORY;
3958 PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
3959 if (pIntNet)
3960 {
3961 //pIntNet->pNetworks = NULL;
3962
3963 rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
3964 if (RT_SUCCESS(rc))
3965 {
3966 rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
3967 UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
3968 if (RT_SUCCESS(rc))
3969 {
3970 *ppIntNet = pIntNet;
3971 LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
3972 return VINF_SUCCESS;
3973 }
3974
3975 RTSemFastMutexDestroy(pIntNet->FastMutex);
3976 }
3977 RTMemFree(pIntNet);
3978 }
3979 *ppIntNet = NULL;
3980 LogFlow(("INTNETR0Create: returns %Rrc\n", rc));
3981 return rc;
3982}
3983
Note: See TracBrowser for help on using the repository browser.

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