VirtualBox

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

Last change on this file since 14272 was 13435, checked in by vboxsync, 16 years ago

w64 unwind hacking: renamed SUPDRV_WITH_UNWIND_HACK to RT_WITH_W64_UNWIND_HACK, fixed license headers and file properties, and some minor adjustments.

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

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