VirtualBox

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

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

intnet: Fixed bug in intnetR0SgReadByte found testing badly segement buffers.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 142.9 KB
Line 
1/* $Id: SrvIntNetR0.cpp 11124 2008-08-04 21:07:40Z 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 Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
1283/** @todo optimize for the case where we can expect both the IP and UDP header to be in the first segment. */
1284
1285 /* check if the protocol is UDP */
1286 if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_p))
1287 != RTNETIPV4_PROT_UDP)
1288 return;
1289
1290 /* get the TCP header length */
1291 uint8_t b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
1292 uint32_t cbIpHdr = (b & 0x0f) * 4;
1293 if (cbIpHdr < RTNETIPV4_MIN_LEN)
1294 return;
1295
1296 /* get the lower byte of the UDP source port number. */
1297 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport) + 1);
1298 if ( b != RTNETIPV4_PORT_BOOTPS
1299 && b != RTNETIPV4_PORT_BOOTPC)
1300 return;
1301 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_sport));
1302 if (b)
1303 return;
1304
1305 /* get the lower byte of the UDP destination port number. */
1306 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport) + 1);
1307 if ( b != RTNETIPV4_PORT_BOOTPS
1308 && b != RTNETIPV4_PORT_BOOTPC)
1309 return;
1310 b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_OFFSETOF(RTNETUDP, uh_dport));
1311 if (b)
1312 return;
1313 intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
1314 break;
1315 }
1316
1317 case RTNET_ETHERTYPE_IPV6:
1318 {
1319 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1320 * need to be edited. Check out how NDP works... */
1321 break;
1322 }
1323
1324 case RTNET_ETHERTYPE_ARP:
1325 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
1326 break;
1327 }
1328}
1329#endif /* INTNET_WITH_DHCP_SNOOPING */
1330
1331
1332/**
1333 * Deals with an IPv4 packet.
1334 *
1335 * This will fish out the source IP address and add it to the cache.
1336 * Then it will look for DHCPRELEASE requests (?) and anything else
1337 * that we migh find useful later.
1338 *
1339 * @param pIf The interface that's sending the frame.
1340 * @param pIpHdr Pointer to the IPv4 header in the frame.
1341 * @param cbPacket The size of the packet, or more correctly the
1342 * size of the frame without the ethernet header.
1343 */
1344static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket)
1345{
1346 /*
1347 * Check the header size first to prevent access invalid data.
1348 */
1349 if (cbPacket < RTNETIPV4_MIN_LEN)
1350 return;
1351 uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
1352 if ( cbHdr < RTNETIPV4_MIN_LEN
1353 || cbPacket < cbHdr)
1354 return;
1355
1356 /*
1357 * If the source address is good (not broadcast or my network) and
1358 * not already in the address cache of the sender, add it. Validate
1359 * the IP header before adding it.
1360 */
1361 bool fValidatedIpHdr = false;
1362 RTNETADDRU Addr;
1363 Addr.IPv4 = pIpHdr->ip_src;
1364 if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
1365 && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
1366 {
1367 if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1368 {
1369 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
1370 return;
1371 }
1372 intnetR0IfAddrCacheAddIt(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, "if/ipv4");
1373 fValidatedIpHdr = true;
1374 }
1375
1376#ifdef INTNET_WITH_DHCP_SNOOPING
1377 /*
1378 * Check for potential DHCP packets.
1379 */
1380 if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
1381 && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
1382 {
1383 PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
1384 if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
1385 || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
1386 && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
1387 || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
1388 {
1389 if ( fValidatedIpHdr
1390 || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket))
1391 intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
1392 else
1393 Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
1394 }
1395 }
1396#endif /* INTNET_WITH_DHCP_SNOOPING */
1397}
1398
1399
1400/**
1401 * Snoop up source addresses from an ARP request or reply.
1402 *
1403 * @param pIf The interface that's sending the frame.
1404 * @param pHdr The ARP header.
1405 * @param cbPacket The size of the packet (migth be larger than the ARP
1406 * request 'cause of min ethernet frame size).
1407 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1408 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1409 */
1410static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
1411{
1412 /*
1413 * Ignore packets which doesn't interest us or we perceive as malformed.
1414 */
1415 if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
1416 return;
1417 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1418 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1419 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1420 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1421 return;
1422 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1423 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1424 && ar_oper != RTNET_ARPOP_REPLY))
1425 {
1426 Log6(("ar_oper=%#x\n", ar_oper));
1427 return;
1428 }
1429
1430 /*
1431 * Tag the SG as ARP IPv4 for later editing, then check for addresses
1432 * which can be removed or added to the address cache of the sender.
1433 */
1434 *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
1435
1436 if ( ar_oper == RTNET_ARPOP_REPLY
1437 && !(pArpIPv4->ar_tha.au8[0] & 1)
1438 && ( pArpIPv4->ar_tha.au16[0]
1439 || pArpIPv4->ar_tha.au16[1]
1440 || pArpIPv4->ar_tha.au16[2])
1441 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
1442 intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1443 (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
1444
1445 if ( !memcmp(&pArpIPv4->ar_sha, &pIf->Mac, sizeof(RTMAC))
1446 && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
1447 intnetR0IfAddrCacheAdd(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
1448 (PCRTNETADDRU)&pArpIPv4->ar_spa, sizeof(RTNETADDRIPV4), "if/arp");
1449}
1450
1451
1452
1453/**
1454 * Checks packets send by a normal interface for new network
1455 * layer addresses.
1456 *
1457 * @param pIf The interface that's sending the frame.
1458 * @param pbFrame The frame.
1459 * @param cbFrame The size of the frame.
1460 * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
1461 * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
1462 */
1463static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, uint16_t *pfSgFlags)
1464{
1465 /*
1466 * Fish out the ethertype and look for stuff we can handle.
1467 */
1468 if (cbFrame <= sizeof(RTNETETHERHDR))
1469 return;
1470 cbFrame -= sizeof(RTNETETHERHDR);
1471
1472 uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
1473 switch (EtherType)
1474 {
1475 case RTNET_ETHERTYPE_IPV4:
1476 intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame);
1477 break;
1478#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
1479 case RTNET_ETHERTYPE_IPV6:
1480 /** @todo IPv6: Check for ICMPv6. It looks like type 133 (Router solicitation) might
1481 * need to be edited. Check out how NDP works... */
1482 intnetR0IfSnoopIPv6SourceAddr(pIf, (PCINTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1483 break;
1484#endif
1485#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
1486 case RTNET_ETHERTYPE_IPX_1:
1487 case RTNET_ETHERTYPE_IPX_2:
1488 case RTNET_ETHERTYPE_IPX_3:
1489 intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1490 break;
1491#endif
1492 case RTNET_ETHERTYPE_ARP:
1493 intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
1494 break;
1495 }
1496}
1497
1498
1499#ifdef IN_INTNET_TESTCASE
1500/**
1501 * Reads the next frame in the buffer.
1502 * The caller is responsible for ensuring that there is a valid frame in the buffer.
1503 *
1504 * @returns Size of the frame in bytes.
1505 * @param pBuf The buffer.
1506 * @param pRingBuff The ring buffer to read from.
1507 * @param pvFrame Where to put the frame. The caller is responsible for
1508 * ensuring that there is sufficient space for the frame.
1509 */
1510static unsigned intnetR0RingReadFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, void *pvFrame)
1511{
1512 Assert(pRingBuf->offRead < pBuf->cbBuf);
1513 Assert(pRingBuf->offRead >= pRingBuf->offStart);
1514 Assert(pRingBuf->offRead < pRingBuf->offEnd);
1515 uint32_t offRead = pRingBuf->offRead;
1516 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offRead);
1517 const void *pvFrameIn = INTNETHdrGetFramePtr(pHdr, pBuf);
1518 unsigned cb = pHdr->cbFrame;
1519 memcpy(pvFrame, pvFrameIn, cb);
1520
1521 /* skip the frame */
1522 offRead += pHdr->offFrame + cb;
1523 offRead = RT_ALIGN_32(offRead, sizeof(INTNETHDR));
1524 Assert(offRead <= pRingBuf->offEnd && offRead >= pRingBuf->offStart);
1525 if (offRead >= pRingBuf->offEnd)
1526 offRead = pRingBuf->offStart;
1527 ASMAtomicXchgU32(&pRingBuf->offRead, offRead);
1528 return cb;
1529}
1530#endif /* IN_INTNET_TESTCASE */
1531
1532
1533/**
1534 * Writes a frame packet to the buffer.
1535 *
1536 * @returns VBox status code.
1537 * @param pBuf The buffer.
1538 * @param pRingBuf The ring buffer to read from.
1539 * @param pSG The gather list.
1540 * @param pNewDstMac Set the destination MAC address to the address if specified.
1541 */
1542static int intnetR0RingWriteFrame(PINTNETBUF pBuf, PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
1543{
1544 /*
1545 * Validate input.
1546 */
1547 AssertPtr(pBuf);
1548 AssertPtr(pRingBuf);
1549 AssertPtr(pSG);
1550 Assert(pSG->cbTotal >= sizeof(RTMAC) * 2);
1551 uint32_t offWrite = pRingBuf->offWrite;
1552 Assert(offWrite == RT_ALIGN_32(offWrite, sizeof(INTNETHDR)));
1553 uint32_t offRead = pRingBuf->offRead;
1554 Assert(offRead == RT_ALIGN_32(offRead, sizeof(INTNETHDR)));
1555
1556 const uint32_t cb = RT_ALIGN_32(pSG->cbTotal, sizeof(INTNETHDR));
1557 if (offRead <= offWrite)
1558 {
1559 /*
1560 * Try fit it all before the end of the buffer.
1561 */
1562 if (pRingBuf->offEnd - offWrite >= cb + sizeof(INTNETHDR))
1563 {
1564 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1565 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1566 pHdr->cbFrame = pSG->cbTotal;
1567 pHdr->offFrame = sizeof(INTNETHDR);
1568
1569 intnetR0SgRead(pSG, pHdr + 1);
1570 if (pNewDstMac)
1571 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1572
1573 offWrite += cb + sizeof(INTNETHDR);
1574 Assert(offWrite <= pRingBuf->offEnd && offWrite >= pRingBuf->offStart);
1575 if (offWrite >= pRingBuf->offEnd)
1576 offWrite = pRingBuf->offStart;
1577 Log2(("WriteFrame: offWrite: %#x -> %#x (1)\n", pRingBuf->offWrite, offWrite));
1578 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1579 return VINF_SUCCESS;
1580 }
1581
1582 /*
1583 * Try fit the frame at the start of the buffer.
1584 * (The header fits before the end of the buffer because of alignment.)
1585 */
1586 AssertMsg(pRingBuf->offEnd - offWrite >= sizeof(INTNETHDR), ("offEnd=%x offWrite=%x\n", pRingBuf->offEnd, offWrite));
1587 if (offRead - pRingBuf->offStart > cb) /* not >= ! */
1588 {
1589 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1590 void *pvFrameOut = (PINTNETHDR)((uint8_t *)pBuf + pRingBuf->offStart);
1591 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1592 pHdr->cbFrame = pSG->cbTotal;
1593 pHdr->offFrame = (intptr_t)pvFrameOut - (intptr_t)pHdr;
1594
1595 intnetR0SgRead(pSG, pvFrameOut);
1596 if (pNewDstMac)
1597 ((PRTNETETHERHDR)pvFrameOut)->DstMac = *pNewDstMac;
1598
1599 offWrite = pRingBuf->offStart + cb;
1600 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1601 Log2(("WriteFrame: offWrite: %#x -> %#x (2)\n", pRingBuf->offWrite, offWrite));
1602 return VINF_SUCCESS;
1603 }
1604 }
1605 /*
1606 * The reader is ahead of the writer, try fit it into that space.
1607 */
1608 else if (offRead - offWrite > cb + sizeof(INTNETHDR)) /* not >= ! */
1609 {
1610 PINTNETHDR pHdr = (PINTNETHDR)((uint8_t *)pBuf + offWrite);
1611 pHdr->u16Type = INTNETHDR_TYPE_FRAME;
1612 pHdr->cbFrame = pSG->cbTotal;
1613 pHdr->offFrame = sizeof(INTNETHDR);
1614
1615 intnetR0SgRead(pSG, pHdr + 1);
1616 if (pNewDstMac)
1617 ((PRTNETETHERHDR)(pHdr + 1))->DstMac = *pNewDstMac;
1618
1619 offWrite += cb + sizeof(INTNETHDR);
1620 ASMAtomicXchgU32(&pRingBuf->offWrite, offWrite);
1621 Log2(("WriteFrame: offWrite: %#x -> %#x (3)\n", pRingBuf->offWrite, offWrite));
1622 return VINF_SUCCESS;
1623 }
1624
1625 /* (it didn't fit) */
1626 /** @todo stats */
1627 return VERR_BUFFER_OVERFLOW;
1628}
1629
1630
1631/**
1632 * Sends a frame to a specific interface.
1633 *
1634 * @param pIf The interface.
1635 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1636 * @param pSG The gather buffer which data is being sent to the interface.
1637 * @param pNewDstMac Set the destination MAC address to the address if specified.
1638 */
1639static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
1640{
1641// LogFlow(("intnetR0IfSend: pIf=%p:{.hIf=%RX32}\n", pIf, pIf->hIf));
1642 int rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1643 if (RT_SUCCESS(rc))
1644 {
1645 pIf->cYields = 0;
1646 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1647 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1648 RTSemEventSignal(pIf->Event);
1649 return;
1650 }
1651
1652 Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
1653
1654#if 0 /* This is bad stuff now as we're blocking while locking down the network.
1655 we really shouldn't delay the network traffic on the host just because
1656 some bugger isn't responding. Will have to deal with this in a different
1657 manner if required. */
1658 /*
1659 * Retry a few times, yielding the CPU in between.
1660 * But don't let a unresponsive VM harm performance, so give up after a couple of tries.
1661 */
1662 if ( pIf->fActive
1663 && pIf->cYields < 100)
1664 {
1665 unsigned cYields = 10;
1666#else
1667 /*
1668 * Scheduling hack, for unicore machines primarily.
1669 */
1670 if ( pIf->fActive
1671 && pIf->cYields < 4 /* just twice */
1672 && pIfSender /* but not if it's from the trunk */)
1673 {
1674 unsigned cYields = 2;
1675#endif
1676 while (--cYields > 0)
1677 {
1678 RTSemEventSignal(pIf->Event);
1679 RTThreadYield();
1680 rc = intnetR0RingWriteFrame(pIf->pIntBuf, &pIf->pIntBuf->Recv, pSG, pNewDstMac);
1681 if (RT_SUCCESS(rc))
1682 {
1683 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
1684 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatRecvs);
1685 STAM_REL_COUNTER_ADD(&pIf->pIntBuf->cbStatRecv, pSG->cbTotal);
1686 RTSemEventSignal(pIf->Event);
1687 return;
1688 }
1689 pIf->cYields++;
1690 }
1691 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
1692 }
1693
1694 /* ok, the frame is lost. */
1695 STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
1696 RTSemEventSignal(pIf->Event);
1697}
1698
1699
1700/**
1701 * Sends a frame down the trunk.
1702 *
1703 * The caller must own the network mutex, might be abandond temporarily.
1704 * The fTrunkLock parameter indicates whether the trunk lock is held.
1705 *
1706 * @param pThis The trunk.
1707 * @param pNetwork The network the frame is being sent to.
1708 * @param pIfSender The IF sending the frame. Used for MAC address checks in shared MAC mode.
1709 * @param fDst The destination flags.
1710 * @param pSG Pointer to the gather list.
1711 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1712 */
1713static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
1714 uint32_t fDst, PINTNETSG pSG, bool fTrunkLocked)
1715{
1716 /*
1717 * Quick sanity check.
1718 */
1719 AssertPtr(pThis);
1720 AssertPtr(pNetwork);
1721 AssertPtr(pSG);
1722 Assert(fDst);
1723 AssertReturnVoid(pThis->pIfPort);
1724
1725 /*
1726 * Edit the frame if we're sharing the MAC address with the host on the wire.
1727 *
1728 * If the frame is headed for both the host and the wire, we'll have to send
1729 * it to the host before making any modifications, and force the OS specific
1730 * backend to copy it. We do this by marking it as TEMP (which is always the
1731 * case right now).
1732 */
1733 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1734 && (fDst & INTNETTRUNKDIR_WIRE))
1735 {
1736 /* Dispatch it to the host before making changes. */
1737 if (fDst & INTNETTRUNKDIR_HOST)
1738 {
1739 Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
1740 intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG, fTrunkLocked);
1741 fDst &= ~INTNETTRUNKDIR_HOST;
1742 }
1743
1744 /* ASSUME frame from INTNETR0IfSend! */
1745 AssertReturnVoid(pSG->cSegsUsed == 1);
1746 AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
1747 AssertReturnVoid(fTrunkLocked);
1748 AssertReturnVoid(pIfSender);
1749 PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
1750
1751 /*
1752 * Get the host mac address and update the ethernet header.
1753 *
1754 * The reason for caching it in the trunk structure is because
1755 * we cannot take the trunk out-bound semaphore when we make
1756 * edits in the intnetR0TrunkIfPortRecv path.
1757 */
1758 pThis->pIfPort->pfnGetMacAddress(pThis->pIfPort, &pThis->CachedMac);
1759 if (!memcmp(&pEthHdr->SrcMac, &pIfSender->Mac, sizeof(RTMAC)))
1760 pEthHdr->SrcMac = pThis->CachedMac;
1761
1762 /*
1763 * Deal with tags from the snooping phase.
1764 */
1765 if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
1766 {
1767 /*
1768 * APR IPv4: replace hardware (MAC) addresses because these end up
1769 * in ARP caches. So, if we don't the other machiens will
1770 * send the packets to the MAC address of the guest
1771 * instead of the one of the host, which won't work on
1772 * wireless of course...
1773 */
1774 PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
1775 if (!memcmp(&pArp->ar_sha, &pIfSender->Mac, sizeof(RTMAC)))
1776 {
1777 Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->CachedMac));
1778 pArp->ar_sha = pThis->CachedMac;
1779 }
1780 if (!memcmp(&pArp->ar_tha, &pIfSender->Mac, sizeof(RTMAC))) /* just in case... */
1781 {
1782 Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->CachedMac));
1783 pArp->ar_tha = pThis->CachedMac;
1784 }
1785 }
1786 //else if (pSG->fFlags & INTNETSG_FLAGS_ICMPV6_NDP)
1787 //{ /// @todo move the editing into a different function
1788 //}
1789 }
1790
1791 /*
1792 * Temporarily leave the network lock while transmitting the frame.
1793 *
1794 * Note that we're relying on the out-bound lock to serialize threads down
1795 * in INTNETR0IfSend. It's theoretically possible for there to be race now
1796 * because I didn't implement async SG handling yet. Which is why we currently
1797 * require the trunk to be locked, well, one of the reasons.
1798 *
1799 * Another reason is that the intnetR0NetworkSendUnicast code may have to
1800 * call into the trunk interface component to do package switching.
1801 */
1802 AssertReturnVoid(fTrunkLocked); /* to be removed. */
1803
1804 int rc;
1805 if ( fTrunkLocked
1806 || intnetR0TrunkIfRetain(pThis))
1807 {
1808 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
1809 AssertRC(rc);
1810 if (RT_SUCCESS(rc))
1811 {
1812 if ( fTrunkLocked
1813 || intnetR0TrunkIfOutLock(pThis))
1814 {
1815 rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pSG, fDst);
1816
1817 if (!fTrunkLocked)
1818 intnetR0TrunkIfOutUnlock(pThis);
1819 }
1820 else
1821 {
1822 AssertFailed();
1823 rc = VERR_SEM_DESTROYED;
1824 }
1825
1826 int rc2 = RTSemFastMutexRequest(pNetwork->FastMutex);
1827 AssertRC(rc2);
1828 }
1829
1830 if (!fTrunkLocked)
1831 intnetR0TrunkIfRelease(pThis);
1832 }
1833 else
1834 {
1835 AssertFailed();
1836 rc = VERR_SEM_DESTROYED;
1837 }
1838
1839 /** @todo failure statistics? */
1840 Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst));
1841}
1842
1843
1844/**
1845 * Edits an ARP packet arriving from the wire via the trunk connection.
1846 *
1847 * @param pNetwork The network the frame is being sent to.
1848 * @param pSG Pointer to the gather list for the frame.
1849 * The flags and data content may be updated.
1850 * @param pEthHdr Pointer to the ethernet header. This may also be
1851 * updated if it's a unicast...
1852 */
1853static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
1854{
1855 /*
1856 * Check the minimum size and get a linear copy of the thing to work on,
1857 * using the temporary buffer if necessary.
1858 */
1859 if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
1860 return;
1861 PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
1862 if ( pSG->cSegsUsed != 1
1863 && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
1864 {
1865 Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
1866 if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
1867 return;
1868 pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
1869 pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
1870 }
1871
1872 /*
1873 * Ignore packets which doesn't interest us or we perceive as malformed.
1874 */
1875 if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
1876 || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
1877 || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
1878 || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
1879 return;
1880 uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
1881 if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
1882 && ar_oper != RTNET_ARPOP_REPLY))
1883 {
1884 Log6(("ar_oper=%#x\n", ar_oper));
1885 return;
1886 }
1887
1888 /* Tag it as ARP IPv4. */
1889 pSG->fFlags |= INTNETSG_FLAGS_ARP_IPV4;
1890
1891 /*
1892 * The thing we're interested in here is a reply to a query made by a guest
1893 * since we modified the MAC in the initial request the guest made.
1894 */
1895 if ( ar_oper == RTNET_ARPOP_REPLY
1896 && !memcmp(&pArpIPv4->ar_tha, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1897 {
1898 PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
1899 kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
1900 if (pIf)
1901 {
1902 Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->Mac));
1903 pArpIPv4->ar_tha = pIf->Mac;
1904 if (!memcmp(&pEthHdr->DstMac, &pNetwork->pTrunkIF->CachedMac, sizeof(RTMAC)))
1905 {
1906 Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
1907 pEthHdr->DstMac = pIf->Mac;
1908 if ((void *)pEthHdr != pSG->aSegs[0].pv)
1909 intnetR0SgWritePart(pSG, RT_OFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->Mac);
1910 }
1911
1912 /* Write back the packet if we've been making changes to a buffered copy. */
1913 if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
1914 intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
1915 }
1916 }
1917}
1918
1919
1920/**
1921 * Sends a broadcast frame.
1922 *
1923 * The caller must own the network mutex, might be abandond temporarily.
1924 * When pIfSender is not NULL, the caller must also own the trunk semaphore.
1925 *
1926 * @returns true if it's addressed to someone on the network, otherwise false.
1927 * @param pNetwork The network the frame is being sent to.
1928 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1929 * @param fSrc The source flags. This 0 if it's not from the trunk.
1930 * @param pSG Pointer to the gather list.
1931 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1932 * @param pEthHdr Pointer to the ethernet header.
1933 */
1934static bool intnetR0NetworkSendBroadcast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
1935 PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
1936{
1937 /*
1938 * Check for ARP packets from the wire since we'll have to make
1939 * modification to them if we're sharing the MAC address with the host.
1940 */
1941 if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1942 && (fSrc & INTNETTRUNKDIR_WIRE)
1943 && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP)
1944 intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
1945
1946 /*
1947 * This is a broadcast or multicast address. For the present we treat those
1948 * two as the same - investigating multicast is left for later.
1949 *
1950 * Write the packet to all the interfaces and signal them.
1951 */
1952 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
1953 if (pIf != pIfSender)
1954 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
1955
1956 /*
1957 * Unless the trunk is the origin, broadcast it to both the wire
1958 * and the host as well.
1959 */
1960 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
1961 if ( pIfSender
1962 && pTrunkIf)
1963 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked);
1964
1965 /*
1966 * Snoop address info from packet orginating from the trunk connection.
1967 */
1968 else if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
1969 && !pIfSender)
1970 {
1971#ifdef INTNET_WITH_DHCP_SNOOPING
1972 uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
1973 if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
1974 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
1975 || (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4)) )
1976 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
1977#else
1978 if (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4))
1979 intnetR0TrunkIfSnoopArp(pNetwork, pSG);
1980#endif
1981 }
1982
1983 return false; /* broadcast frames are never dropped */
1984}
1985
1986
1987/**
1988 * Sends a multicast frame.
1989 *
1990 * The caller must own the network mutex, might be abandond temporarily.
1991 *
1992 * @returns true if it's addressed to someone on the network, otherwise false.
1993 * @param pNetwork The network the frame is being sent to.
1994 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
1995 * @param fSrc The source flags. This 0 if it's not from the trunk.
1996 * @param pSG Pointer to the gather list.
1997 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
1998 * @param pEthHdr Pointer to the ethernet header.
1999 */
2000static bool intnetR0NetworkSendMulticast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
2001{
2002 /** @todo implement multicast */
2003 return intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, pEthHdr);
2004}
2005
2006
2007/**
2008 * Sends a unicast frame using the network layer address instead
2009 * of the link layer one.
2010 *
2011 * The caller must own the network mutex, might be abandond temporarily.
2012 *
2013 * @returns true if it's addressed to someone on the network, otherwise false.
2014 * @param pNetwork The network the frame is being sent to.
2015 * @param pSG Pointer to the gather list.
2016 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2017 * @param pEthHdr Pointer to the ethernet header.
2018 */
2019static bool intnetR0NetworkSendUnicastWithSharedMac(PINTNETNETWORK pNetwork, PINTNETSG pSG, bool fTrunkLocked, PRTNETETHERHDR pEthHdr)
2020{
2021 /*
2022 * Extract the network address from the packet.
2023 */
2024 RTNETADDRU Addr;
2025 INTNETADDRTYPE enmAddrType;
2026 uint8_t cbAddr;
2027 switch (RT_BE2H_U16(pEthHdr->EtherType))
2028 {
2029 case RTNET_ETHERTYPE_IPV4:
2030 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
2031 {
2032 Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
2033 return false;
2034 }
2035 enmAddrType = kIntNetAddrType_IPv4;
2036 cbAddr = sizeof(Addr.IPv4);
2037 Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
2038 break;
2039
2040#if 0 /** @todo IntNet: implement IPv6 for wireless MAC sharing. */
2041 case RTNET_ETHERTYPE_IPV6
2042 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
2043 {
2044 Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
2045 return false;
2046 }
2047 enmAddrType = kIntNetAddrType_IPv6;
2048 cbAddr = sizeof(Addr.IPv6);
2049 break;
2050#endif
2051#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
2052 case RTNET_ETHERTYPE_IPX_1:
2053 case RTNET_ETHERTYPE_IPX_2:
2054 case RTNET_ETHERTYPE_IPX_3:
2055 if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
2056 {
2057 Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
2058 return false;
2059 }
2060 enmAddrType = kIntNetAddrType_IPX;
2061 cbAddr = sizeof(Addr.IPX);
2062 break;
2063#endif
2064
2065 /*
2066 * Treat ARP is broadcast (it shouldn't end up here normally,
2067 * so it goes last in the switch).
2068 */
2069 case RTNET_ETHERTYPE_ARP:
2070 Log6(("intnetshareduni: ARP\n"));
2071 /** @todo revisit this broadcasting of unicast ARP frames! */
2072 return intnetR0NetworkSendBroadcast(pNetwork, NULL, INTNETTRUNKDIR_WIRE, pSG, fTrunkLocked, pEthHdr);
2073
2074 /*
2075 * Unknown packets are sent do all interfaces that are in promiscuous mode.
2076 */
2077 default:
2078 {
2079 Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
2080 if (!(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC)))
2081 {
2082 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2083 if (pIf->fPromiscuous)
2084 {
2085 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2086 intnetR0IfSend(pIf, NULL, pSG, NULL);
2087 }
2088 }
2089 return false;
2090 }
2091 }
2092
2093 /*
2094 * Send it to interfaces with matching network addresses.
2095 */
2096 bool fExactIntNetRecipient = false;
2097 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2098 {
2099 bool fIt = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmAddrType], &Addr, cbAddr) >= 0;
2100 if ( fIt
2101 || ( pIf->fPromiscuous
2102 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))))
2103 {
2104 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2105 fExactIntNetRecipient |= fIt;
2106 intnetR0IfSend(pIf, NULL, pSG, fIt ? &pIf->Mac : NULL);
2107 }
2108 }
2109
2110#ifdef INTNET_WITH_DHCP_SNOOPING
2111 /*
2112 * Perform DHCP snooping.
2113 */
2114 if ( enmAddrType == kIntNetAddrType_IPv4
2115 && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN)
2116 intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
2117#endif /* INTNET_WITH_DHCP_SNOOPING */
2118
2119 return fExactIntNetRecipient;
2120}
2121
2122
2123/**
2124 * Sends a unicast frame.
2125 *
2126 * The caller must own the network mutex, might be abandond temporarily.
2127 *
2128 * @returns true if it's addressed to someone on the network, otherwise false.
2129 * @param pNetwork The network the frame is being sent to.
2130 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2131 * @param fSrc The source flags. This 0 if it's not from the trunk.
2132 * @param pSG Pointer to the gather list.
2133 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2134 * @param pEthHdr Pointer to the ethernet header.
2135 */
2136static bool intnetR0NetworkSendUnicast(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked, PCRTNETETHERHDR pEthHdr)
2137{
2138 /*
2139 * Only send to the interfaces with matching a MAC address.
2140 */
2141 bool fExactIntNetRecipient = false;
2142 for (PINTNETIF pIf = pNetwork->pIFs; pIf; pIf = pIf->pNext)
2143 {
2144 bool fIt = false;
2145 if ( ( !pIf->fMacSet
2146 || (fIt = !memcmp(&pIf->Mac, &pEthHdr->DstMac, sizeof(pIf->Mac))) )
2147 || ( pIf->fPromiscuous
2148 && !(pNetwork->fFlags & (INTNET_OPEN_FLAGS_IGNORE_PROMISC | INTNET_OPEN_FLAGS_QUIETLY_IGNORE_PROMISC))
2149 && pIf != pIfSender /* promiscuous mode: omit the sender */))
2150 {
2151 Log2(("Dst=%.6Rhxs => %.6Rhxs\n", &pEthHdr->DstMac, &pIf->Mac));
2152 fExactIntNetRecipient |= fIt;
2153 intnetR0IfSend(pIf, pIfSender, pSG, NULL);
2154 }
2155 }
2156
2157 /*
2158 * Send it to the trunk?
2159 * If we didn't find the recipient on the internal network the
2160 * frame will hit the wire.
2161 */
2162 uint32_t fDst = 0;
2163 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2164 if ( pIfSender
2165 && pTrunkIf
2166 && pTrunkIf->pIfPort)
2167 {
2168 Assert(!fSrc);
2169
2170 /* promiscuous checks first as they are cheaper than pfnIsHostMac. */
2171 if ( pTrunkIf->fPromiscuousWire
2172 && !(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)) )
2173 fDst |= INTNETTRUNKDIR_WIRE;
2174 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))
2175 || pTrunkIf->pIfPort->pfnIsPromiscuous(pTrunkIf->pIfPort) )
2176 fDst |= INTNETTRUNKDIR_HOST;
2177
2178 if ( fDst != (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
2179 && !fExactIntNetRecipient /* if you have duplicate mac addresses, you're screwed. */ )
2180 {
2181 if (pTrunkIf->pIfPort->pfnIsHostMac(pTrunkIf->pIfPort, &pEthHdr->DstMac))
2182 fDst |= INTNETTRUNKDIR_HOST;
2183 else
2184 fDst |= INTNETTRUNKDIR_WIRE;
2185 }
2186
2187 if (fDst)
2188 intnetR0TrunkIfSend(pTrunkIf, pNetwork, pIfSender, fDst, pSG, fTrunkLocked);
2189 }
2190
2191 /* log it */
2192 if ( !fExactIntNetRecipient
2193 && !fDst
2194 && ( (pEthHdr->DstMac.au8[0] == 0x08 && pEthHdr->DstMac.au8[1] == 0x00 && pEthHdr->DstMac.au8[2] == 0x27)
2195 || (pEthHdr->SrcMac.au8[0] == 0x08 && pEthHdr->SrcMac.au8[1] == 0x00 && pEthHdr->SrcMac.au8[2] == 0x27)))
2196 Log2(("Dst=%.6Rhxs ??\n", &pEthHdr->DstMac));
2197
2198 return fExactIntNetRecipient;
2199}
2200
2201
2202/**
2203 * Sends a frame.
2204 *
2205 * This function will distribute the frame to the interfaces it is addressed to.
2206 * It will also update the MAC address of the sender.
2207 *
2208 * The caller must own the network mutex.
2209 *
2210 * @returns true if it's addressed to someone on the network, otherwise false.
2211 * @param pNetwork The network the frame is being sent to.
2212 * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
2213 * @param fSrc The source flags. This 0 if it's not from the trunk.
2214 * @param pSG Pointer to the gather list.
2215 * @param fTrunkLocked Whether the caller owns the out-bound trunk lock.
2216 */
2217static bool intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc, PINTNETSG pSG, bool fTrunkLocked)
2218{
2219 bool fRc = false;
2220
2221 /*
2222 * Assert reality.
2223 */
2224 AssertPtr(pNetwork);
2225 AssertPtrNull(pIfSender);
2226 Assert(pIfSender ? fSrc == 0 : fSrc != 0);
2227 Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
2228 AssertPtr(pSG);
2229 Assert(pSG->cSegsUsed >= 1);
2230 Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
2231 if (pSG->cbTotal < sizeof(RTNETETHERHDR))
2232 return fRc;
2233
2234 /*
2235 * Send statistics.
2236 */
2237 if (pIfSender)
2238 {
2239 STAM_REL_COUNTER_INC(&pIfSender->pIntBuf->cStatSends);
2240 STAM_REL_COUNTER_ADD(&pIfSender->pIntBuf->cbStatSend, pSG->cbTotal);
2241 }
2242
2243 /*
2244 * Get the ethernet header (might theoretically involve multiple segments).
2245 */
2246 RTNETETHERHDR EthHdr;
2247 if (pSG->aSegs[0].cb >= sizeof(EthHdr))
2248 EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
2249 else if (!intnetR0SgReadPart(pSG, 0, sizeof(EthHdr), &EthHdr))
2250 return false;
2251 if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
2252 || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
2253 || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
2254 || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
2255 || EthHdr.DstMac.au8[0] == 0xff
2256 || EthHdr.SrcMac.au8[0] == 0xff)
2257 Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
2258 &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
2259
2260 /*
2261 * Inspect the header updating the mac address of the sender in the process.
2262 */
2263 if ( pIfSender
2264 && memcmp(&EthHdr.SrcMac, &pIfSender->Mac, sizeof(pIfSender->Mac)))
2265 {
2266 /** @todo stats */
2267 Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->Mac, &EthHdr.SrcMac));
2268 pIfSender->Mac = EthHdr.SrcMac;
2269 pIfSender->fMacSet = true;
2270 }
2271
2272 /*
2273 * Distribute the frame.
2274 */
2275 if ( EthHdr.DstMac.au16[0] == 0xffff /* broadcast address. */
2276 && EthHdr.DstMac.au16[1] == 0xffff
2277 && EthHdr.DstMac.au16[2] == 0xffff)
2278 fRc = intnetR0NetworkSendBroadcast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2279 else if (RT_UNLIKELY(EthHdr.DstMac.au8[0] & 1)) /* multicast address */
2280 fRc = intnetR0NetworkSendMulticast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2281 else if ( !(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2282 || !(fSrc & INTNETTRUNKDIR_WIRE))
2283 fRc = intnetR0NetworkSendUnicast(pNetwork, pIfSender, fSrc, pSG, fTrunkLocked, &EthHdr);
2284 else
2285 fRc = intnetR0NetworkSendUnicastWithSharedMac(pNetwork, pSG, fTrunkLocked, &EthHdr);
2286 return fRc;
2287}
2288
2289
2290/**
2291 * Sends one or more frames.
2292 *
2293 * The function will first the frame which is passed as the optional
2294 * arguments pvFrame and cbFrame. These are optional since it also
2295 * possible to chain together one or more frames in the send buffer
2296 * which the function will process after considering it's arguments.
2297 *
2298 * @returns VBox status code.
2299 * @param pIntNet The instance data.
2300 * @param hIf The interface handle.
2301 * @param pSession The caller's session.
2302 * @param pvFrame Pointer to the frame. Optional, please don't use.
2303 * @param cbFrame Size of the frame. Optional, please don't use.
2304 */
2305INTNETR0DECL(int) INTNETR0IfSend(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, const void *pvFrame, unsigned cbFrame)
2306{
2307 Log5(("INTNETR0IfSend: pIntNet=%p hIf=%RX32 pvFrame=%p cbFrame=%u\n", pIntNet, hIf, pvFrame, cbFrame));
2308
2309 /*
2310 * Validate input and translate the handle.
2311 */
2312 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2313 if (pvFrame && cbFrame)
2314 {
2315 AssertReturn(cbFrame < 0x8000, VERR_INVALID_PARAMETER);
2316 AssertPtrReturn(pvFrame, VERR_INVALID_PARAMETER);
2317 AssertPtrReturn((uint8_t *)pvFrame + cbFrame - 1, VERR_INVALID_PARAMETER);
2318
2319 /* This is the better place to crash, probe the buffer. */
2320 ASMProbeReadBuffer(pvFrame, cbFrame);
2321 }
2322 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2323 if (!pIf)
2324 return VERR_INVALID_HANDLE;
2325
2326 /*
2327 * Lock the network. If there is a trunk retain it and grab its
2328 * out-bound lock (this requires leaving the network lock first).
2329 * Grabbing the out-bound lock here simplifies things quite a bit
2330 * later on, so while this is excessive and a bit expensive it's
2331 * not worth caring about right now.
2332 */
2333 PINTNETNETWORK pNetwork = pIf->pNetwork;
2334 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2335 if (RT_FAILURE(rc))
2336 {
2337 intnetR0IfRelease(pIf, pSession);
2338 return rc;
2339 }
2340 PINTNETTRUNKIF pTrunkIf = intnetR0TrunkIfRetain(pNetwork->pTrunkIF);
2341 if (pTrunkIf)
2342 {
2343 RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2344
2345 if (!intnetR0TrunkIfOutLock(pTrunkIf))
2346 {
2347 intnetR0TrunkIfRelease(pTrunkIf);
2348 intnetR0IfRelease(pIf, pSession);
2349 return VERR_SEM_DESTROYED;
2350 }
2351
2352 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2353 if (RT_FAILURE(rc))
2354 {
2355 intnetR0TrunkIfOutUnlock(pTrunkIf);
2356 intnetR0TrunkIfRelease(pTrunkIf);
2357 intnetR0IfRelease(pIf, pSession);
2358 return rc;
2359 }
2360 }
2361
2362 INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
2363 * with buffer sharing for some OS or service. Darwin copies everything so
2364 * I won't bother allocating and managing SGs rigth now. Sorry. */
2365
2366 /*
2367 * Process the argument.
2368 */
2369 if (pvFrame && cbFrame)
2370 {
2371 intnetR0SgInitTemp(&Sg, (void *)pvFrame, cbFrame);
2372 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2373 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvFrame, cbFrame, (uint16_t *)&Sg.fFlags);
2374 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2375 }
2376
2377 /*
2378 * Process the send buffer.
2379 */
2380 while (pIf->pIntBuf->Send.offRead != pIf->pIntBuf->Send.offWrite)
2381 {
2382 /* Send the frame if the type is sane. */
2383 PINTNETHDR pHdr = (PINTNETHDR)((uintptr_t)pIf->pIntBuf + pIf->pIntBuf->Send.offRead);
2384 if (pHdr->u16Type == INTNETHDR_TYPE_FRAME)
2385 {
2386 void *pvCurFrame = INTNETHdrGetFramePtr(pHdr, pIf->pIntBuf);
2387 if (pvCurFrame)
2388 {
2389 intnetR0SgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
2390 if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
2391 intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, (uint16_t *)&Sg.fFlags);
2392 intnetR0NetworkSend(pNetwork, pIf, 0, &Sg, !!pTrunkIf);
2393 }
2394 }
2395 /* else: ignore the frame */
2396
2397 /* Skip to the next frame. */
2398 INTNETRingSkipFrame(pIf->pIntBuf, &pIf->pIntBuf->Send);
2399 }
2400
2401 /*
2402 * Release the semaphore(s) and release references.
2403 */
2404 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2405 if (pTrunkIf)
2406 {
2407 intnetR0TrunkIfOutUnlock(pTrunkIf);
2408 intnetR0TrunkIfRelease(pTrunkIf);
2409 }
2410
2411 intnetR0IfRelease(pIf, pSession);
2412 return rc;
2413}
2414
2415
2416/**
2417 * VMMR0 request wrapper for INTNETR0IfSend.
2418 *
2419 * @returns see INTNETR0IfSend.
2420 * @param pIntNet The internal networking instance.
2421 * @param pSession The caller's session.
2422 * @param pReq The request packet.
2423 */
2424INTNETR0DECL(int) INTNETR0IfSendReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
2425{
2426 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2427 return VERR_INVALID_PARAMETER;
2428 return INTNETR0IfSend(pIntNet, pReq->hIf, pSession, NULL, 0);
2429}
2430
2431
2432/**
2433 * Maps the default buffer into ring 3.
2434 *
2435 * @returns VBox status code.
2436 * @param pIntNet The instance data.
2437 * @param hIf The interface handle.
2438 * @param pSession The caller's session.
2439 * @param ppRing3Buf Where to store the address of the ring-3 mapping.
2440 */
2441INTNETR0DECL(int) INTNETR0IfGetRing3Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, R3PTRTYPE(PINTNETBUF) *ppRing3Buf)
2442{
2443 LogFlow(("INTNETR0IfGetRing3Buffer: pIntNet=%p hIf=%RX32 ppRing3Buf=%p\n", pIntNet, hIf, ppRing3Buf));
2444
2445 /*
2446 * Validate input.
2447 */
2448 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2449 AssertPtrReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
2450 *ppRing3Buf = 0;
2451 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2452 if (!pIf)
2453 return VERR_INVALID_HANDLE;
2454
2455 /*
2456 * ASSUMES that only the process that created an interface can use it.
2457 * ASSUMES that we created the ring-3 mapping when selecting or
2458 * allocating the buffer.
2459 */
2460 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2461 if (RT_SUCCESS(rc))
2462 {
2463 *ppRing3Buf = pIf->pIntBufR3;
2464 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2465 }
2466
2467 intnetR0IfRelease(pIf, pSession);
2468 LogFlow(("INTNETR0IfGetRing3Buffer: returns %Rrc *ppRing3Buf=%p\n", rc, *ppRing3Buf));
2469 return rc;
2470}
2471
2472
2473/**
2474 * VMMR0 request wrapper for INTNETR0IfGetRing3Buffer.
2475 *
2476 * @returns see INTNETR0IfGetRing3Buffer.
2477 * @param pIntNet The internal networking instance.
2478 * @param pSession The caller's session.
2479 * @param pReq The request packet.
2480 */
2481INTNETR0DECL(int) INTNETR0IfGetRing3BufferReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFGETRING3BUFFERREQ pReq)
2482{
2483 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2484 return VERR_INVALID_PARAMETER;
2485 return INTNETR0IfGetRing3Buffer(pIntNet, pReq->hIf, pSession, &pReq->pRing3Buf);
2486}
2487
2488
2489/**
2490 * Gets the ring-0 address of the current buffer.
2491 *
2492 * @returns VBox status code.
2493 * @param pIntNet The instance data.
2494 * @param hIf The interface handle.
2495 * @param pSession The caller's session.
2496 * @param ppRing0Buf Where to store the address of the ring-3 mapping.
2497 */
2498INTNETR0DECL(int) INTNETR0IfGetRing0Buffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF *ppRing0Buf)
2499{
2500 LogFlow(("INTNETR0IfGetRing0Buffer: pIntNet=%p hIf=%RX32 ppRing0Buf=%p\n", pIntNet, hIf, ppRing0Buf));
2501
2502 /*
2503 * Validate input.
2504 */
2505 AssertPtrReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
2506 *ppRing0Buf = NULL;
2507 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2508 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2509 if (!pIf)
2510 return VERR_INVALID_HANDLE;
2511
2512 /*
2513 * Grab the lock and get the data.
2514 * ASSUMES that the handle isn't closed while we're here.
2515 */
2516 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2517 if (RT_SUCCESS(rc))
2518 {
2519 *ppRing0Buf = pIf->pIntBuf;
2520
2521 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2522 }
2523 intnetR0IfRelease(pIf, pSession);
2524 LogFlow(("INTNETR0IfGetRing0Buffer: returns %Rrc *ppRing0Buf=%p\n", rc, *ppRing0Buf));
2525 return rc;
2526}
2527
2528
2529#if 0
2530/**
2531 * Gets the physical addresses of the default interface buffer.
2532 *
2533 * @returns VBox status code.
2534 * @param pIntNet The instance data.
2535 * @param hIF The interface handle.
2536 * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
2537 * @param cPages
2538 */
2539INTNETR0DECL(int) INTNETR0IfGetPhysBuffer(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
2540{
2541 /*
2542 * Validate input.
2543 */
2544 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2545 AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
2546 AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
2547 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2548 if (!pIf)
2549 return VERR_INVALID_HANDLE;
2550
2551 /*
2552 * Grab the lock and get the data.
2553 * ASSUMES that the handle isn't closed while we're here.
2554 */
2555 int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
2556 if (RT_SUCCESS(rc))
2557 {
2558 /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
2559 * is no need for any extra bookkeeping here.. */
2560
2561 rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
2562 }
2563 intnetR0IfRelease(pIf, pSession);
2564 return VERR_NOT_IMPLEMENTED;
2565}
2566#endif
2567
2568
2569/**
2570 * Sets the promiscuous mode property of an interface.
2571 *
2572 * @returns VBox status code.
2573 * @param pIntNet The instance handle.
2574 * @param hIf The interface handle.
2575 * @param pSession The caller's session.
2576 * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
2577 */
2578INTNETR0DECL(int) INTNETR0IfSetPromiscuousMode(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
2579{
2580 LogFlow(("INTNETR0IfSetPromiscuousMode: pIntNet=%p hIf=%RX32 fPromiscuous=%d\n", pIntNet, hIf, fPromiscuous));
2581
2582 /*
2583 * Validate & translate input.
2584 */
2585 AssertReturn(pIntNet, VERR_INVALID_PARAMETER);
2586 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2587 if (!pIf)
2588 {
2589 Log(("INTNETR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
2590 return VERR_INVALID_HANDLE;
2591 }
2592
2593 /*
2594 * Grab the network semaphore and make the change.
2595 */
2596 int rc;
2597 PINTNETNETWORK pNetwork = pIf->pNetwork;
2598 if (pNetwork)
2599 {
2600 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2601 if (RT_SUCCESS(rc))
2602 {
2603 if (pIf->fPromiscuous != fPromiscuous)
2604 {
2605 Log(("INTNETR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d\n",
2606 hIf, !fPromiscuous, !!fPromiscuous));
2607 ASMAtomicUoWriteBool(&pIf->fPromiscuous, fPromiscuous);
2608 }
2609
2610 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2611 }
2612 }
2613 else
2614 rc = VERR_WRONG_ORDER;
2615
2616 intnetR0IfRelease(pIf, pSession);
2617 return rc;
2618}
2619
2620
2621/**
2622 * VMMR0 request wrapper for INTNETR0IfSetPromiscuousMode.
2623 *
2624 * @returns see INTNETR0IfSetPromiscuousMode.
2625 * @param pIntNet The internal networking instance.
2626 * @param pSession The caller's session.
2627 * @param pReq The request packet.
2628 */
2629INTNETR0DECL(int) INTNETR0IfSetPromiscuousModeReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
2630{
2631 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2632 return VERR_INVALID_PARAMETER;
2633 return INTNETR0IfSetPromiscuousMode(pIntNet, pReq->hIf, pSession, pReq->fPromiscuous);
2634}
2635
2636
2637/**
2638 * Sets the MAC address of an interface.
2639 *
2640 * @returns VBox status code.
2641 * @param pIntNet The instance handle.
2642 * @param hIf The interface handle.
2643 * @param pSession The caller's session.
2644 * @param pMAC The new MAC address.
2645 */
2646INTNETR0DECL(int) INTNETR0IfSetMacAddress(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCPDMMAC pMac)
2647{
2648 LogFlow(("INTNETR0IfSetMacAddress: pIntNet=%p hIf=%RX32 pMac=%p:{%.6Rhxs}\n", pIntNet, hIf, pMac, pMac));
2649
2650 /*
2651 * Validate & translate input.
2652 */
2653 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2654 AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
2655 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2656 if (!pIf)
2657 {
2658 Log(("INTNETR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
2659 return VERR_INVALID_HANDLE;
2660 }
2661
2662 /*
2663 * Grab the network semaphore and make the change.
2664 */
2665 int rc;
2666 PINTNETNETWORK pNetwork = pIf->pNetwork;
2667 if (pNetwork)
2668 {
2669 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
2670 if (RT_SUCCESS(rc))
2671 {
2672 if (memcmp(&pIf->Mac, pMac, sizeof(pIf->Mac)))
2673 {
2674 Log(("INTNETR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
2675 hIf, &pIf->Mac, pMac));
2676 pIf->Mac = *pMac;
2677 pIf->fMacSet = true;
2678 }
2679
2680 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
2681 }
2682 }
2683 else
2684 rc = VERR_WRONG_ORDER;
2685
2686 intnetR0IfRelease(pIf, pSession);
2687 return rc;
2688}
2689
2690
2691/**
2692 * VMMR0 request wrapper for INTNETR0IfSetMacAddress.
2693 *
2694 * @returns see INTNETR0IfSetMacAddress.
2695 * @param pIntNet The internal networking instance.
2696 * @param pSession The caller's session.
2697 * @param pReq The request packet.
2698 */
2699INTNETR0DECL(int) INTNETR0IfSetMacAddressReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
2700{
2701 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2702 return VERR_INVALID_PARAMETER;
2703 return INTNETR0IfSetMacAddress(pIntNet, pReq->hIf, pSession, &pReq->Mac);
2704}
2705
2706
2707/**
2708 * Worker for intnetR0IfSetActive.
2709 *
2710 * This function will update the active interface count on the network and
2711 * activate or deactivate the trunk connection if necessary. Note that in
2712 * order to do this it is necessary to abandond the network semaphore.
2713 *
2714 * @returns VBox status code.
2715 * @param pNetwork The network.
2716 * @param fIf The interface.
2717 * @param fActive What to do.
2718 */
2719static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
2720{
2721 /* quick santiy check */
2722 AssertPtr(pNetwork);
2723 AssertPtr(pIf);
2724
2725 /*
2726 * If we've got a trunk, lock it now in case we need to call out, and
2727 * then lock the network.
2728 */
2729 PINTNETTRUNKIF pTrunkIf = pNetwork->pTrunkIF;
2730 if (pTrunkIf && !intnetR0TrunkIfOutLock(pTrunkIf))
2731 return VERR_SEM_DESTROYED;
2732
2733 int rc = RTSemFastMutexRequest(pNetwork->FastMutex); AssertRC(rc);
2734 if (RT_SUCCESS(rc))
2735 {
2736 bool fNetworkLocked = true;
2737
2738 /*
2739 * Make the change if necessary.
2740 */
2741 if (pIf->fActive != fActive)
2742 {
2743 pIf->fActive = fActive;
2744
2745 uint32_t const cActiveIFs = pNetwork->cActiveIFs;
2746 Assert((int32_t)cActiveIFs + (fActive ? 1 : -1) >= 0);
2747 pNetwork->cActiveIFs += fActive ? 1 : -1;
2748
2749 if ( pTrunkIf
2750 && ( !pNetwork->cActiveIFs
2751 || !cActiveIFs))
2752 {
2753 /*
2754 * We'll have to change the trunk status, so, leave
2755 * the network semaphore so we don't create any deadlocks.
2756 */
2757 int rc2 = RTSemFastMutexRelease(pNetwork->FastMutex); AssertRC(rc2);
2758 fNetworkLocked = false;
2759
2760 if (pTrunkIf->pIfPort)
2761 pTrunkIf->pIfPort->pfnSetActive(pTrunkIf->pIfPort, fActive);
2762 }
2763 }
2764
2765 if (fNetworkLocked)
2766 RTSemFastMutexRelease(pNetwork->FastMutex);
2767 }
2768 if (pTrunkIf)
2769 intnetR0TrunkIfOutUnlock(pTrunkIf);
2770 return rc;
2771}
2772
2773
2774/**
2775 * Activates or deactivates a interface.
2776 *
2777 * This is used to enable and disable the trunk connection on demans as well as
2778 * know when not to expect an interface to want to receive packets.
2779 *
2780 * @returns VBox status code.
2781 * @param pIf The interface.
2782 * @param fActive What to do.
2783 */
2784static int intnetR0IfSetActive(PINTNETIF pIf, bool fActive)
2785{
2786 /* quick sanity check */
2787 AssertPtrReturn(pIf, VERR_INVALID_POINTER);
2788
2789 /*
2790 * Hand it to the network since it might involve the trunk
2791 * and things are tricky there wrt to locking order.
2792 */
2793 PINTNETNETWORK pNetwork = pIf->pNetwork;
2794 if (!pNetwork)
2795 return VERR_WRONG_ORDER;
2796 return intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2797}
2798
2799
2800/**
2801 * Sets the active property of an interface.
2802 *
2803 * @returns VBox status code.
2804 * @param pIntNet The instance handle.
2805 * @param hIf The interface handle.
2806 * @param pSession The caller's session.
2807 * @param fActive The new state.
2808 */
2809INTNETR0DECL(int) INTNETR0IfSetActive(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
2810{
2811 LogFlow(("INTNETR0IfSetActive: pIntNet=%p hIf=%RX32 fActive=%RTbool\n", pIntNet, hIf, fActive));
2812
2813 /*
2814 * Validate & translate input.
2815 */
2816 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2817 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2818 if (!pIf)
2819 {
2820 Log(("INTNETR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
2821 return VERR_INVALID_HANDLE;
2822 }
2823
2824 /*
2825 * Hand it to the network since it might involve the trunk
2826 * and things are tricky there wrt to locking order.
2827 */
2828 int rc;
2829 PINTNETNETWORK pNetwork = pIf->pNetwork;
2830 if (pNetwork)
2831 rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
2832 else
2833 rc = VERR_WRONG_ORDER;
2834
2835 intnetR0IfRelease(pIf, pSession);
2836 return rc;
2837}
2838
2839
2840/**
2841 * VMMR0 request wrapper for INTNETR0IfSetActive.
2842 *
2843 * @returns see INTNETR0IfSetActive.
2844 * @param pIntNet The internal networking instance.
2845 * @param pSession The caller's session.
2846 * @param pReq The request packet.
2847 */
2848INTNETR0DECL(int) INTNETR0IfSetActiveReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
2849{
2850 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2851 return VERR_INVALID_PARAMETER;
2852 return INTNETR0IfSetActive(pIntNet, pReq->hIf, pSession, pReq->fActive);
2853}
2854
2855
2856/**
2857 * Wait for the interface to get signaled.
2858 * The interface will be signaled when is put into the receive buffer.
2859 *
2860 * @returns VBox status code.
2861 * @param pIntNet The instance handle.
2862 * @param hIf The interface handle.
2863 * @param pSession The caller's session.
2864 * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
2865 * used if indefinite wait is desired.
2866 */
2867INTNETR0DECL(int) INTNETR0IfWait(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
2868{
2869 Log4(("INTNETR0IfWait: pIntNet=%p hIf=%RX32 cMillies=%u\n", pIntNet, hIf, cMillies));
2870
2871 /*
2872 * Get and validate essential handles.
2873 */
2874 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2875 PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
2876 if (!pIf)
2877 {
2878 Log(("INTNETR0IfWait: returns VERR_INVALID_HANDLE\n"));
2879 return VERR_INVALID_HANDLE;
2880 }
2881 const INTNETIFHANDLE hIfSelf = pIf->hIf;
2882 const RTSEMEVENT Event = pIf->Event;
2883 if ( hIfSelf != hIf /* paranoia */
2884 && Event != NIL_RTSEMEVENT)
2885 {
2886 Log(("INTNETR0IfWait: returns VERR_SEM_DESTROYED\n"));
2887 return VERR_SEM_DESTROYED;
2888 }
2889
2890 /*
2891 * It is tempting to check if there is data to be read here,
2892 * but the problem with such an approach is that it will cause
2893 * one unnecessary supervisor->user->supervisor trip. There is
2894 * already a slight risk for such, so no need to increase it.
2895 */
2896
2897 /*
2898 * Increment the number of waiters before starting the wait.
2899 * Upon wakeup we must assert reality, checking that we're not
2900 * already destroyed or in the process of being destroyed. This
2901 * code must be aligned with the waiting code in intnetR0IfDestruct.
2902 */
2903 ASMAtomicIncU32(&pIf->cSleepers);
2904 int rc = RTSemEventWaitNoResume(Event, cMillies);
2905 if (pIf->Event == Event)
2906 {
2907 ASMAtomicDecU32(&pIf->cSleepers);
2908 if (!pIf->fDestroying)
2909 {
2910 intnetR0IfRelease(pIf, pSession);
2911 if (pIf->hIf != hIf)
2912 rc = VERR_SEM_DESTROYED;
2913 }
2914 else
2915 rc = VERR_SEM_DESTROYED;
2916 }
2917 else
2918 rc = VERR_SEM_DESTROYED;
2919 Log4(("INTNETR0IfWait: returns %Rrc\n", rc));
2920 return rc;
2921}
2922
2923
2924/**
2925 * VMMR0 request wrapper for INTNETR0IfWait.
2926 *
2927 * @returns see INTNETR0IfWait.
2928 * @param pIntNet The internal networking instance.
2929 * @param pSession The caller's session.
2930 * @param pReq The request packet.
2931 */
2932INTNETR0DECL(int) INTNETR0IfWaitReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
2933{
2934 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2935 return VERR_INVALID_PARAMETER;
2936 return INTNETR0IfWait(pIntNet, pReq->hIf, pSession, pReq->cMillies);
2937}
2938
2939
2940/**
2941 * Close an interface.
2942 *
2943 * @returns VBox status code.
2944 * @param pIntNet The instance handle.
2945 * @param hIf The interface handle.
2946 * @param pSession The caller's session.
2947 */
2948INTNETR0DECL(int) INTNETR0IfClose(PINTNET pIntNet, INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
2949{
2950 LogFlow(("INTNETR0IfClose: pIntNet=%p hIf=%RX32\n", pIntNet, hIf));
2951
2952 /*
2953 * Validate and free the handle.
2954 */
2955 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
2956 PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
2957 if (!pIf)
2958 return VERR_INVALID_HANDLE;
2959
2960 /* mark the handle as freed so intnetR0IfDestruct won't free it again. */
2961 ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
2962
2963
2964 /*
2965 * Release the references to the interface object (handle + free lookup).
2966 * But signal the event semaphore first so any waiter holding a reference
2967 * will wake up too (he'll see hIf == invalid and return correctly).
2968 */
2969 RTSemEventSignal(pIf->Event);
2970
2971 void *pvObj = pIf->pvObj;
2972 intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
2973
2974 int rc = SUPR0ObjRelease(pvObj, pSession);
2975 LogFlow(("INTNETR0IfClose: returns %Rrc\n", rc));
2976 return rc;
2977}
2978
2979
2980/**
2981 * VMMR0 request wrapper for INTNETR0IfCloseReq.
2982 *
2983 * @returns see INTNETR0IfClose.
2984 * @param pIntNet The internal networking instance.
2985 * @param pSession The caller's session.
2986 * @param pReq The request packet.
2987 */
2988INTNETR0DECL(int) INTNETR0IfCloseReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
2989{
2990 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
2991 return VERR_INVALID_PARAMETER;
2992 return INTNETR0IfClose(pIntNet, pReq->hIf, pSession);
2993}
2994
2995
2996/**
2997 * Interface destructor callback.
2998 * This is called for reference counted objectes when the count reaches 0.
2999 *
3000 * @param pvObj The object pointer.
3001 * @param pvUser1 Pointer to the interface.
3002 * @param pvUser2 Pointer to the INTNET instance data.
3003 */
3004static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3005{
3006 PINTNETIF pIf = (PINTNETIF)pvUser1;
3007 PINTNET pIntNet = (PINTNET)pvUser2;
3008 Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
3009
3010 RTSemFastMutexRequest(pIntNet->FastMutex);
3011
3012 /*
3013 * Mark the interface as being destroyed so the waiter
3014 * can behave appropriately (theoretical case).
3015 */
3016 ASMAtomicWriteBool(&pIf->fDestroying, true);
3017
3018 /*
3019 * Delete the interface handle so the object no longer can be used.
3020 * (Can happen if the client didn't close its session.)
3021 */
3022 INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
3023 if (hIf != INTNET_HANDLE_INVALID)
3024 {
3025 void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession);
3026 AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
3027 }
3028
3029 /*
3030 * If we've got a network deactivate and unlink ourselves from it.
3031 * Because of cleanup order we might be an orphan now.
3032 */
3033 PINTNETNETWORK pNetwork = pIf->pNetwork;
3034 if (pNetwork)
3035 {
3036 intnetR0IfSetActive(pIf, false);
3037
3038 if (pNetwork->pIFs == pIf)
3039 pNetwork->pIFs = pIf->pNext;
3040 else
3041 {
3042 PINTNETIF pPrev = pNetwork->pIFs;
3043 while (pPrev)
3044 {
3045 if (pPrev->pNext == pIf)
3046 {
3047 pPrev->pNext = pIf->pNext;
3048 break;
3049 }
3050 pPrev = pPrev->pNext;
3051 }
3052 Assert(pPrev);
3053 }
3054 pIf->pNext = NULL;
3055
3056 /*
3057 * Release our reference to the network.
3058 */
3059 RTSemFastMutexRelease(pIntNet->FastMutex);
3060
3061 SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
3062 pIf->pNetwork = NULL;
3063 }
3064 else
3065 RTSemFastMutexRelease(pIntNet->FastMutex);
3066
3067 /*
3068 * Wakeup anyone waiting on this interface.
3069 *
3070 * We *must* make sure they have woken up properly and realized
3071 * that the interface is no longer valid.
3072 */
3073 if (pIf->Event != NIL_RTSEMEVENT)
3074 {
3075 RTSEMEVENT Event = pIf->Event;
3076 unsigned cMaxWait = 0x1000;
3077 while (pIf->cSleepers && cMaxWait-- > 0)
3078 {
3079 RTSemEventSignal(Event);
3080 RTThreadYield();
3081 }
3082 if (pIf->cSleepers)
3083 {
3084 RTThreadSleep(1);
3085
3086 cMaxWait = pIf->cSleepers;
3087 while (pIf->cSleepers && cMaxWait-- > 0)
3088 {
3089 RTSemEventSignal(Event);
3090 RTThreadSleep(10);
3091 }
3092 }
3093
3094 RTSemEventDestroy(Event);
3095 pIf->Event = NIL_RTSEMEVENT;
3096 }
3097
3098 /*
3099 * Unmap user buffer.
3100 */
3101 if (pIf->pIntBuf != pIf->pIntBufDefault)
3102 {
3103 /** @todo user buffer */
3104 }
3105
3106 /*
3107 * Unmap and Free the default buffer.
3108 */
3109 if (pIf->pIntBufDefault)
3110 {
3111 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
3112 pIf->pIntBufDefault = NULL;
3113 pIf->pIntBufDefaultR3 = 0;
3114 pIf->pIntBuf = NULL;
3115 pIf->pIntBufR3 = 0;
3116 }
3117
3118 /*
3119 * The interface.
3120 */
3121 pIf->pvObj = NULL;
3122 RTMemFree(pIf);
3123}
3124
3125
3126/**
3127 * Creates a new network interface.
3128 *
3129 * The call must have opened the network for the new interface
3130 * and is responsible for closing it on failure. On success
3131 * it must leave the network opened so the interface destructor
3132 * can close it.
3133 *
3134 * @returns VBox status code.
3135 * @param pNetwork The network.
3136 * @param pSession The session handle.
3137 * @param cbSend The size of the send buffer.
3138 * @param cbRecv The size of the receive buffer.
3139 * @param phIf Where to store the interface handle.
3140 */
3141static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv, bool *pfCloseNetwork, PINTNETIFHANDLE phIf)
3142{
3143 LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u phIf=%p\n",
3144 pNetwork, pSession, cbSend, cbRecv, phIf));
3145
3146 /*
3147 * Assert input.
3148 */
3149 AssertPtr(pNetwork);
3150 AssertPtr(phIf);
3151 AssertPtr(pfCloseNetwork);
3152 *pfCloseNetwork = false;
3153
3154 /*
3155 * Allocate and initialize the interface structure.
3156 */
3157 PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
3158 if (!pIf)
3159 return VERR_NO_MEMORY;
3160 //pIf->pNext = NULL;
3161 memset(&pIf->Mac, 0xff, sizeof(pIf->Mac)); /* broadcast */
3162 //pIf->fMacSet = false;
3163 //pIf->fPromiscuous = false;
3164 //pIf->fActive = false;
3165 //pIf->fDestroying = false;
3166 //pIf->pIntBuf = 0;
3167 //pIf->pIntBufR3 = NIL_RTR3PTR;
3168 //pIf->pIntBufDefault = 0;
3169 //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
3170 //pIf->cYields = 0;
3171 pIf->Event = NIL_RTSEMEVENT;
3172 //pIf->cSleepers = 0;
3173 pIf->hIf = INTNET_HANDLE_INVALID;
3174 pIf->pNetwork = pNetwork;
3175 pIf->pSession = pSession;
3176 //pIf->pvObj = NULL;
3177 //pIf->aAddrCache[kIntNetAddrType_Invalid] = {0};
3178 //pIf->aAddrCache[kIntNetAddrType_IPv4].pbEntries = NULL;
3179 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntries = 0;
3180 //pIf->aAddrCache[kIntNetAddrType_IPv4].cEntriesAlloc = 0;
3181 pIf->aAddrCache[kIntNetAddrType_IPv4].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv4);
3182 pIf->aAddrCache[kIntNetAddrType_IPv4].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv4);
3183 //pIf->aAddrCache[kIntNetAddrType_IPv6].pbEntries = NULL;
3184 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntries = 0;
3185 //pIf->aAddrCache[kIntNetAddrType_IPv6].cEntriesAlloc = 0;
3186 pIf->aAddrCache[kIntNetAddrType_IPv6].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPv6);
3187 pIf->aAddrCache[kIntNetAddrType_IPv6].cbEntry = intnetR0AddrSize(kIntNetAddrType_IPv6);
3188 //pIf->aAddrCache[kIntNetAddrType_IPX].pbEntries = NULL;
3189 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntries = 0;
3190 //pIf->aAddrCache[kIntNetAddrType_IPX].cEntriesAlloc = 0;
3191 pIf->aAddrCache[kIntNetAddrType_IPX].cbAddress = intnetR0AddrSize(kIntNetAddrType_IPX);
3192 pIf->aAddrCache[kIntNetAddrType_IPX].cbEntry = RT_ALIGN_32(intnetR0AddrSize(kIntNetAddrType_IPv4), 16);
3193 int rc = RTSemEventCreate((PRTSEMEVENT)&pIf->Event);
3194 if (RT_SUCCESS(rc))
3195 {
3196 /*
3197 * Create the default buffer.
3198 */
3199 /** @todo adjust with minimums and apply defaults here. */
3200 cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3201 cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), sizeof(INTNETHDR));
3202 const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR)) + cbRecv + cbSend;
3203 rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
3204 if (RT_SUCCESS(rc))
3205 {
3206 ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
3207
3208 pIf->pIntBuf = pIf->pIntBufDefault;
3209 pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
3210 pIf->pIntBuf->cbBuf = cbBuf;
3211 pIf->pIntBuf->cbRecv = cbRecv;
3212 pIf->pIntBuf->cbSend = cbSend;
3213 /* receive ring buffer. */
3214 pIf->pIntBuf->Recv.offStart = RT_ALIGN_32(sizeof(*pIf->pIntBuf), sizeof(INTNETHDR));
3215 pIf->pIntBuf->Recv.offRead = pIf->pIntBuf->Recv.offStart;
3216 pIf->pIntBuf->Recv.offWrite = pIf->pIntBuf->Recv.offStart;
3217 pIf->pIntBuf->Recv.offEnd = pIf->pIntBuf->Recv.offStart + cbRecv;
3218 /* send ring buffer. */
3219 pIf->pIntBuf->Send.offStart = pIf->pIntBuf->Recv.offEnd;
3220 pIf->pIntBuf->Send.offRead = pIf->pIntBuf->Send.offStart;
3221 pIf->pIntBuf->Send.offWrite = pIf->pIntBuf->Send.offStart;
3222 pIf->pIntBuf->Send.offEnd = pIf->pIntBuf->Send.offStart + cbSend;
3223
3224 /*
3225 * Link the interface to the network.
3226 */
3227 rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3228 if (RT_SUCCESS(rc))
3229 {
3230 pIf->pNext = pNetwork->pIFs;
3231 pNetwork->pIFs = pIf;
3232 RTSemFastMutexRelease(pNetwork->FastMutex);
3233
3234 /*
3235 * Register the interface with the session.
3236 */
3237 pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE, intnetR0IfDestruct, pIf, pNetwork->pIntNet);
3238 if (pIf->pvObj)
3239 {
3240 rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
3241 if (RT_SUCCESS(rc))
3242 {
3243 *phIf = pIf->hIf;
3244 Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
3245 *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
3246 return VINF_SUCCESS;
3247 }
3248
3249 SUPR0ObjRelease(pIf->pvObj, pSession);
3250 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3251 return rc;
3252 }
3253
3254 RTSemFastMutexDestroy(pNetwork->FastMutex);
3255 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3256 }
3257
3258 SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
3259 pIf->pIntBufDefault = NULL;
3260 pIf->pIntBuf = NULL;
3261 }
3262
3263 RTSemEventDestroy(pIf->Event);
3264 pIf->Event = NIL_RTSEMEVENT;
3265 }
3266 RTMemFree(pIf);
3267 LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
3268 *pfCloseNetwork = true;
3269 return rc;
3270}
3271
3272
3273
3274
3275
3276/** @copydoc INTNETTRUNKSWPORT::pfnSetSGPhys */
3277static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
3278{
3279 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3280 AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
3281 return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
3282}
3283
3284
3285/** @copydoc INTNETTRUNKSWPORT::pfnRecv */
3286static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG, uint32_t fSrc)
3287{
3288 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3289 PINTNETNETWORK pNetwork = pThis->pNetwork;
3290
3291 /* assert some sanity */
3292 AssertPtrReturn(pNetwork, false);
3293 AssertReturn(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX, false);
3294 AssertPtr(pSG);
3295 Assert(fSrc);
3296
3297 /*
3298 * Lock the network and send the frame to it.
3299 */
3300 int rc = RTSemFastMutexRequest(pNetwork->FastMutex);
3301 AssertRCReturn(rc, false);
3302
3303 bool fRc;
3304 if (RT_LIKELY(pNetwork->cActiveIFs > 0))
3305 fRc = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, false /* fTrunkLocked */);
3306 else
3307 fRc = false; /* don't drop it */
3308
3309 rc = RTSemFastMutexRelease(pNetwork->FastMutex);
3310 AssertRC(rc);
3311
3312 return fRc;
3313}
3314
3315
3316/** @copydoc INTNETTRUNKSWPORT::pfnSGRetain */
3317static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3318{
3319 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3320 PINTNETNETWORK pNetwork = pThis->pNetwork;
3321
3322 /* assert some sanity */
3323 AssertPtrReturnVoid(pNetwork);
3324 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3325 AssertPtr(pSG);
3326 Assert(pSG->cUsers > 0);
3327
3328 /* do it. */
3329 ++pSG->cUsers;
3330}
3331
3332
3333/** @copydoc INTNETTRUNKSWPORT::pfnSGRelease */
3334static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
3335{
3336 PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
3337 PINTNETNETWORK pNetwork = pThis->pNetwork;
3338
3339 /* assert some sanity */
3340 AssertPtrReturnVoid(pNetwork);
3341 AssertReturnVoid(pNetwork->FastMutex != NIL_RTSEMFASTMUTEX);
3342 AssertPtr(pSG);
3343 Assert(pSG->cUsers > 0);
3344
3345 /*
3346 * Free it?
3347 */
3348 if (!--pSG->cUsers)
3349 {
3350 /** @todo later */
3351 }
3352}
3353
3354
3355/**
3356 * Retain the trunk interface.
3357 *
3358 * @returns pThis if retained.
3359 *
3360 * @param pThis The trunk.
3361 *
3362 * @remarks Any locks.
3363 */
3364static PINTNETTRUNKIF intnetR0TrunkIfRetain(PINTNETTRUNKIF pThis)
3365{
3366 if (pThis && pThis->pIfPort)
3367 {
3368 pThis->pIfPort->pfnRetain(pThis->pIfPort);
3369 return pThis;
3370 }
3371 return NULL;
3372}
3373
3374
3375/**
3376 * Release the trunk interface.
3377 *
3378 * @param pThis The trunk.
3379 */
3380static void intnetR0TrunkIfRelease(PINTNETTRUNKIF pThis)
3381{
3382 if (pThis && pThis->pIfPort)
3383 pThis->pIfPort->pfnRelease(pThis->pIfPort);
3384}
3385
3386
3387/**
3388 * Takes the out-bound trunk lock.
3389 *
3390 * This will ensure that pIfPort is valid.
3391 *
3392 * @returns success indicator.
3393 * @param pThis The trunk.
3394 *
3395 * @remarks No locks other than the create/destroy one.
3396 */
3397static bool intnetR0TrunkIfOutLock(PINTNETTRUNKIF pThis)
3398{
3399 AssertPtrReturn(pThis, false);
3400 int rc = RTSemFastMutexRequest(pThis->FastMutex);
3401 if (RT_SUCCESS(rc))
3402 {
3403 if (RT_LIKELY(pThis->pIfPort))
3404 return true;
3405 RTSemFastMutexRelease(pThis->FastMutex);
3406 }
3407 else
3408 AssertMsg(rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc));
3409 return false;
3410}
3411
3412
3413/**
3414 * Releases the out-bound trunk lock.
3415 *
3416 * @param pThis The trunk.
3417 */
3418static void intnetR0TrunkIfOutUnlock(PINTNETTRUNKIF pThis)
3419{
3420 if (pThis)
3421 {
3422 int rc = RTSemFastMutexRelease(pThis->FastMutex);
3423 AssertRC(rc);
3424 }
3425}
3426
3427
3428/**
3429 * Activates the trunk interface.
3430 *
3431 * @param pThis The trunk.
3432 * @param fActive What to do with it.
3433 *
3434 * @remarks Caller may only own the create/destroy lock.
3435 */
3436static void intnetR0TrunkIfActivate(PINTNETTRUNKIF pThis, bool fActive)
3437{
3438 if (intnetR0TrunkIfOutLock(pThis))
3439 {
3440 pThis->pIfPort->pfnSetActive(pThis->pIfPort, fActive);
3441 intnetR0TrunkIfOutUnlock(pThis);
3442 }
3443}
3444
3445
3446/**
3447 * Shutdown the trunk interface.
3448 *
3449 * @param pThis The trunk.
3450 * @param pNetworks The network.
3451 *
3452 * @remarks The caller must *NOT* hold the network lock. The global
3453 * create/destroy lock is fine though.
3454 */
3455static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
3456{
3457 /* assert sanity */
3458 if (!pThis)
3459 return;
3460 AssertPtr(pThis);
3461 Assert(pThis->pNetwork == pNetwork);
3462 AssertPtrNull(pThis->pIfPort);
3463
3464 /*
3465 * The interface has already been deactivated, we just to wait for
3466 * it to become idle before we can disconnect and release it.
3467 */
3468 PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
3469 if (pIfPort)
3470 {
3471 intnetR0TrunkIfOutLock(pThis);
3472
3473 /* unset it */
3474 pThis->pIfPort = NULL;
3475
3476 /* wait in portions so we can complain ever now an then. */
3477 uint64_t StartTS = RTTimeSystemNanoTS();
3478 int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3479 if (RT_FAILURE(rc))
3480 {
3481 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3482 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3483 Assert(rc == VERR_TIMEOUT);
3484 while ( RT_FAILURE(rc)
3485 && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
3486 rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
3487 if (rc == VERR_TIMEOUT)
3488 {
3489 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc).\n",
3490 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3491 while ( rc == VERR_TIMEOUT
3492 && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
3493 rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
3494 if (RT_FAILURE(rc))
3495 {
3496 LogRel(("intnet: '%s' did't become idle in %RU64 ns (%Rrc), giving up.\n",
3497 pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
3498 AssertRC(rc);
3499 }
3500 }
3501 }
3502
3503 /* disconnect & release it. */
3504 pIfPort->pfnDisconnectAndRelease(pIfPort);
3505 }
3506
3507 /*
3508 * Free up the resources.
3509 */
3510 RTSEMFASTMUTEX FastMutex = pThis->FastMutex;
3511 pThis->FastMutex = NIL_RTSEMFASTMUTEX;
3512 pThis->pNetwork = NULL;
3513 RTSemFastMutexRelease(FastMutex);
3514 RTSemFastMutexDestroy(FastMutex);
3515 RTMemFree(pThis);
3516}
3517
3518
3519/**
3520 * Creates the trunk connection (if any).
3521 *
3522 * @returns VBox status code.
3523 *
3524 * @param pNetwork The newly created network.
3525 * @param pSession The session handle.
3526 */
3527static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3528{
3529 const char *pszName;
3530 switch (pNetwork->enmTrunkType)
3531 {
3532 /*
3533 * The 'None' case, simple.
3534 */
3535 case kIntNetTrunkType_None:
3536 case kIntNetTrunkType_WhateverNone:
3537 return VINF_SUCCESS;
3538
3539 /* Can't happen, but makes GCC happy. */
3540 default:
3541 return VERR_NOT_IMPLEMENTED;
3542
3543 /*
3544 * Translate enum to component factory name.
3545 */
3546 case kIntNetTrunkType_NetFlt:
3547 pszName = "VBoxNetFlt";
3548 break;
3549 case kIntNetTrunkType_NetTap:
3550 pszName = "VBoxNetTap";
3551 break;
3552 case kIntNetTrunkType_SrvNat:
3553 pszName = "VBoxSrvNat";
3554 break;
3555 }
3556
3557 /*
3558 * Allocate the trunk interface.
3559 */
3560 PINTNETTRUNKIF pTrunkIF = (PINTNETTRUNKIF)RTMemAllocZ(sizeof(*pTrunkIF));
3561 if (!pTrunkIF)
3562 return VERR_NO_MEMORY;
3563 pTrunkIF->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
3564 pTrunkIF->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
3565 pTrunkIF->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
3566 pTrunkIF->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
3567 pTrunkIF->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
3568 pTrunkIF->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
3569 //pTrunkIF->pIfPort = NULL;
3570 pTrunkIF->pNetwork = pNetwork;
3571 //pTrunkIF->fPhysSG = false;
3572 //pTrunkIF->fPromiscuousWire = false;
3573 pTrunkIF->CachedMac.au8[0] = 0xfe;
3574 pTrunkIF->CachedMac.au8[1] = 0xff;
3575 pTrunkIF->CachedMac.au8[2] = 0xff;
3576 pTrunkIF->CachedMac.au8[3] = 0xff;
3577 pTrunkIF->CachedMac.au8[4] = 0xff;
3578 pTrunkIF->CachedMac.au8[5] = 0xff;
3579 int rc = RTSemFastMutexCreate(&pTrunkIF->FastMutex);
3580 if (RT_SUCCESS(rc))
3581 {
3582#ifdef IN_RING0 /* (testcase is ring-3) */
3583 /*
3584 * Query the factory we want, then use it create and connect the trunk.
3585 */
3586 PINTNETTRUNKFACTORY pTrunkFactory = NULL;
3587 rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
3588 if (RT_SUCCESS(rc))
3589 {
3590 rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory, pNetwork->szTrunk, &pTrunkIF->SwitchPort, &pTrunkIF->pIfPort);
3591 pTrunkFactory->pfnRelease(pTrunkFactory);
3592 if (RT_SUCCESS(rc))
3593 {
3594 Assert(pTrunkIF->pIfPort);
3595 pNetwork->pTrunkIF = pTrunkIF;
3596 Log(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
3597 pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
3598 return VINF_SUCCESS;
3599 }
3600 }
3601#endif /* IN_RING0 */
3602 RTSemFastMutexDestroy(pTrunkIF->FastMutex);
3603 }
3604 RTMemFree(pTrunkIF);
3605 LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
3606 rc, pszName, pNetwork->szTrunk, pNetwork->szName));
3607 return rc;
3608}
3609
3610
3611
3612/**
3613 * Close a network which was opened/created using intnetR0OpenNetwork()/intnetR0CreateNetwork().
3614 *
3615 * @param pNetwork The network to close.
3616 * @param pSession The session handle.
3617 */
3618static int intnetR0NetworkClose(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
3619{
3620 LogFlow(("intnetR0NetworkClose: pNetwork=%p pSession=%p\n", pNetwork, pSession));
3621 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
3622 AssertPtrReturn(pNetwork, VERR_INVALID_PARAMETER);
3623
3624 int rc = SUPR0ObjRelease(pNetwork->pvObj, pSession);
3625 LogFlow(("intnetR0NetworkClose: return %Rrc\n", rc));
3626 return rc;
3627}
3628
3629
3630/**
3631 * Object destructor callback.
3632 * This is called for reference counted objectes when the count reaches 0.
3633 *
3634 * @param pvObj The object pointer.
3635 * @param pvUser1 Pointer to the network.
3636 * @param pvUser2 Pointer to the INTNET instance data.
3637 */
3638static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
3639{
3640 PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
3641 PINTNET pIntNet = (PINTNET)pvUser2;
3642 Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
3643 Assert(pNetwork->pIntNet == pIntNet);
3644
3645 /* take the create/destroy sem. */
3646 RTSemFastMutexRequest(pIntNet->FastMutex);
3647
3648 /*
3649 * Deactivate the trunk connection first (if any).
3650 */
3651 if (pNetwork->pTrunkIF)
3652 intnetR0TrunkIfActivate(pNetwork->pTrunkIF, false /* fActive */);
3653
3654 /*
3655 * Unlink the network.
3656 * Note that it needn't be in the list if we failed during creation.
3657 */
3658 PINTNETNETWORK pPrev = pIntNet->pNetworks;
3659 if (pPrev == pNetwork)
3660 pIntNet->pNetworks = pNetwork->pNext;
3661 else
3662 {
3663 for (; pPrev; pPrev = pPrev->pNext)
3664 if (pPrev->pNext == pNetwork)
3665 {
3666 pPrev->pNext = pNetwork->pNext;
3667 break;
3668 }
3669 }
3670 pNetwork->pNext = NULL;
3671 pNetwork->pvObj = NULL;
3672
3673 /*
3674 * Because of the undefined order of the per session object dereferencing when closing a session,
3675 * we have to handle the case where the network is destroyed before the interfaces. We'll
3676 * deal with this by simply orphaning the interfaces.
3677 */
3678 RTSemFastMutexRequest(pNetwork->FastMutex);
3679
3680 PINTNETIF pCur = pNetwork->pIFs;
3681 while (pCur)
3682 {
3683 PINTNETIF pNext = pCur->pNext;
3684 pCur->pNext = NULL;
3685 pCur->pNetwork = NULL;
3686 pCur = pNext;
3687 }
3688
3689 /* Grab and zap the trunk pointer before leaving the mutex. */
3690 PINTNETTRUNKIF pTrunkIF = pNetwork->pTrunkIF;
3691 pNetwork->pTrunkIF = NULL;
3692
3693 RTSemFastMutexRelease(pNetwork->FastMutex);
3694
3695 /*
3696 * If there is a trunk, delete it.
3697 * Note that this may tak a while if we're unlucky...
3698 */
3699 if (pTrunkIF)
3700 intnetR0TrunkIfDestroy(pTrunkIF, pNetwork);
3701
3702 /*
3703 * Free resources.
3704 */
3705 RTSemFastMutexDestroy(pNetwork->FastMutex);
3706 pNetwork->FastMutex = NIL_RTSEMFASTMUTEX;
3707 RTMemFree(pNetwork);
3708
3709 /* release the create/destroy sem. (can be done before trunk destruction.) */
3710 RTSemFastMutexRelease(pIntNet->FastMutex);
3711}
3712
3713
3714/**
3715 * Opens an existing network.
3716 *
3717 * @returns VBox status code.
3718 * @param pIntNet The instance data.
3719 * @param pSession The current session.
3720 * @param pszNetwork The network name. This has a valid length.
3721 * @param enmTrunkType The trunk type.
3722 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3723 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3724 * @param ppNetwork Where to store the pointer to the network on success.
3725 */
3726static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3727 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3728{
3729 LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3730 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3731
3732 /* just pro forma validation, the caller is internal. */
3733 AssertPtr(pIntNet);
3734 AssertPtr(pSession);
3735 AssertPtr(pszNetwork);
3736 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3737 AssertPtr(pszTrunk);
3738 Assert(!(fFlags & ~(INTNET_OPEN_FLAGS_MASK)));
3739 AssertPtr(ppNetwork);
3740 *ppNetwork = NULL;
3741
3742 /*
3743 * Search networks by name.
3744 */
3745 PINTNETNETWORK pCur;
3746 uint8_t cchName = strlen(pszNetwork);
3747 Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
3748
3749 pCur = pIntNet->pNetworks;
3750 while (pCur)
3751 {
3752 if ( pCur->cchName == cchName
3753 && !memcmp(pCur->szName, pszNetwork, cchName))
3754 {
3755 /*
3756 * Found the network, now check that we have the same ideas
3757 * about the trunk setup and security.
3758 */
3759 int rc;
3760 if ( enmTrunkType == kIntNetTrunkType_WhateverNone
3761 || ( pCur->enmTrunkType == enmTrunkType
3762 && !strcmp(pCur->szTrunk, pszTrunk)))
3763 {
3764 if (!((pCur->fFlags ^ fFlags) & INTNET_OPEN_FLAGS_COMPATIBILITY_XOR_MASK))
3765 {
3766
3767 /*
3768 * Increment the reference and check that the session
3769 * can access this network.
3770 */
3771 rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
3772 if (RT_SUCCESS(rc))
3773 {
3774 if (!(pCur->fFlags & INTNET_OPEN_FLAGS_PUBLIC))
3775 rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
3776 if (RT_SUCCESS(rc))
3777 {
3778 pCur->fFlags |= fFlags & INTNET_OPEN_FLAGS_SECURITY_OR_MASK;
3779
3780 *ppNetwork = pCur;
3781 }
3782 else
3783 SUPR0ObjRelease(pCur->pvObj, pSession);
3784 }
3785 else if (rc == VERR_WRONG_ORDER)
3786 rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
3787 }
3788 else
3789 rc = VERR_INTNET_INCOMPATIBLE_FLAGS;
3790 }
3791 else
3792 rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
3793
3794 LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
3795 return rc;
3796 }
3797 pCur = pCur->pNext;
3798 }
3799
3800 LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
3801 return VERR_NOT_FOUND;
3802}
3803
3804
3805/**
3806 * Creates a new network.
3807 *
3808 * The call must own the INTNET::FastMutex and has already attempted
3809 * opening the network and found it to be non-existing.
3810 *
3811 * @returns VBox status code.
3812 * @param pIntNet The instance data.
3813 * @param pSession The session handle.
3814 * @param pszNetwork The name of the network. This must be at least one character long and no longer
3815 * than the INTNETNETWORK::szName.
3816 * @param enmTrunkType The trunk type.
3817 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3818 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3819 * @param ppNetwork Where to store the network. In the case of failure whatever is returned
3820 * here should be dereferenced outside the INTNET::FastMutex.
3821 */
3822static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
3823 const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
3824{
3825 LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
3826 pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
3827
3828 /* just pro forma validation, the caller is internal. */
3829 AssertPtr(pIntNet);
3830 AssertPtr(pSession);
3831 AssertPtr(pszNetwork);
3832 Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
3833 AssertPtr(pszTrunk);
3834 Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
3835 AssertPtr(ppNetwork);
3836 *ppNetwork = NULL;
3837
3838 /*
3839 * Allocate and initialize.
3840 */
3841 size_t cb = sizeof(INTNETNETWORK);
3842 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3843 cb += INTNETNETWORK_TMP_SIZE + 64;
3844 PINTNETNETWORK pNew = (PINTNETNETWORK)RTMemAllocZ(cb);
3845 if (!pNew)
3846 return VERR_NO_MEMORY;
3847 int rc = RTSemFastMutexCreate(&pNew->FastMutex);
3848 if (RT_SUCCESS(rc))
3849 {
3850 //pNew->pIFs = NULL;
3851 pNew->pIntNet = pIntNet;
3852 //pNew->cActiveIFs = 0;
3853 pNew->fFlags = fFlags;
3854 size_t cchName = strlen(pszNetwork);
3855 pNew->cchName = cchName;
3856 Assert(cchName && cchName < sizeof(pNew->szName)); /* caller's responsibility. */
3857 memcpy(pNew->szName, pszNetwork, cchName); /* '\0' by alloc. */
3858 pNew->enmTrunkType = enmTrunkType;
3859 Assert(strlen(pszTrunk) < sizeof(pNew->szTrunk)); /* caller's responsibility. */
3860 strcpy(pNew->szTrunk, pszTrunk);
3861 if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
3862 pNew->pbTmp = RT_ALIGN_PT(pNew + 1, 64, uint8_t *);
3863 //else
3864 // pNew->pbTmp = NULL;
3865
3866 /*
3867 * Register the object in the current session and link it into the network list.
3868 */
3869 pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNew, pIntNet);
3870 if (pNew->pvObj)
3871 {
3872 pNew->pNext = pIntNet->pNetworks;
3873 pIntNet->pNetworks = pNew;
3874
3875 /*
3876 * Check if the current session is actually allowed to create and open
3877 * the network. It is possible to implement network name based policies
3878 * and these must be checked now. SUPR0ObjRegister does no such checks.
3879 */
3880 rc = SUPR0ObjVerifyAccess(pNew->pvObj, pSession, pNew->szName);
3881 if (RT_SUCCESS(rc))
3882 {
3883 /*
3884 * Connect the trunk.
3885 */
3886 rc = intnetR0NetworkCreateTrunkIf(pNew, pSession);
3887 if (RT_SUCCESS(rc))
3888 {
3889 *ppNetwork = pNew;
3890 LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNew));
3891 return VINF_SUCCESS;
3892 }
3893 }
3894
3895 /*
3896 * We unlink it here so it cannot be opened when the caller leaves
3897 * INTNET::FastMutex before dereferencing it.
3898 */
3899 Assert(pIntNet->pNetworks == pNew);
3900 pIntNet->pNetworks = pNew->pNext;
3901 pNew->pNext = NULL;
3902
3903 *ppNetwork = pNew;
3904 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3905 return rc;
3906 }
3907 rc = VERR_NO_MEMORY;
3908
3909 RTSemFastMutexDestroy(pNew->FastMutex);
3910 pNew->FastMutex = NIL_RTSEMFASTMUTEX;
3911 }
3912 RTMemFree(pNew);
3913 LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
3914 return rc;
3915}
3916
3917
3918/**
3919 * Opens a network interface and connects it to the specified network.
3920 *
3921 * @returns VBox status code.
3922 * @param pIntNet The internal network instance.
3923 * @param pSession The session handle.
3924 * @param pszNetwork The network name.
3925 * @param enmTrunkType The trunk type.
3926 * @param pszTrunk The trunk name. Its meaning is specfic to the type.
3927 * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
3928 * @param fRestrictAccess Whether new participants should be subjected to access check or not.
3929 * @param cbSend The send buffer size.
3930 * @param cbRecv The receive buffer size.
3931 * @param phIf Where to store the handle to the network interface.
3932 */
3933INTNETR0DECL(int) INTNETR0Open(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork,
3934 INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
3935 unsigned cbSend, unsigned cbRecv, PINTNETIFHANDLE phIf)
3936{
3937 LogFlow(("INTNETR0Open: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
3938 pIntNet, pSession, pszNetwork, pszNetwork, pszTrunk, pszTrunk, enmTrunkType, fFlags, cbSend, cbRecv, phIf));
3939
3940 /*
3941 * Validate input.
3942 */
3943 AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
3944
3945 AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
3946 const char *pszNetworkEnd = (const char *)memchr(pszNetwork, '\0', INTNET_MAX_NETWORK_NAME);
3947 AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
3948 size_t cchNetwork = pszNetworkEnd - pszNetwork;
3949 AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
3950
3951 if (pszTrunk)
3952 {
3953 AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
3954 const char *pszTrunkEnd = (const char *)memchr(pszTrunk, '\0', INTNET_MAX_TRUNK_NAME);
3955 AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
3956 }
3957 else
3958 pszTrunk = "";
3959
3960 AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
3961 ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
3962 switch (enmTrunkType)
3963 {
3964 case kIntNetTrunkType_None:
3965 case kIntNetTrunkType_WhateverNone:
3966 AssertReturn(!*pszTrunk, VERR_INVALID_PARAMETER);
3967 break;
3968
3969 case kIntNetTrunkType_NetFlt:
3970 AssertReturn(pszTrunk, VERR_INVALID_PARAMETER);
3971 break;
3972
3973 default:
3974 return VERR_NOT_IMPLEMENTED;
3975 }
3976
3977 AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
3978 AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
3979
3980 /*
3981 * Acquire the mutex to serialize open/create.
3982 */
3983 int rc = RTSemFastMutexRequest(pIntNet->FastMutex);
3984 if (RT_FAILURE(rc))
3985 return rc;
3986
3987 /*
3988 * Try open / create the network and create an interface on it for the caller to use.
3989 *
3990 * Note that because of the destructors grabbing INTNET::FastMutex and us being required
3991 * to own this semaphore for the entire network opening / creation and interface creation
3992 * sequence, intnetR0CreateNetwork will have to defer the network cleanup to us on failure.
3993 */
3994 PINTNETNETWORK pNetwork = NULL;
3995 rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
3996 if (RT_SUCCESS(rc) || rc == VERR_NOT_FOUND)
3997 {
3998 bool fCloseNetwork = true;
3999 if (rc == VERR_NOT_FOUND)
4000 rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
4001 if (RT_SUCCESS(rc))
4002 rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, &fCloseNetwork, phIf);
4003
4004 RTSemFastMutexRelease(pIntNet->FastMutex);
4005
4006 if (RT_FAILURE(rc) && pNetwork && fCloseNetwork)
4007 intnetR0NetworkClose(pNetwork, pSession);
4008 }
4009 else
4010 RTSemFastMutexRelease(pIntNet->FastMutex);
4011
4012 LogFlow(("INTNETR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
4013 return rc;
4014}
4015
4016
4017/**
4018 * VMMR0 request wrapper for GMMR0MapUnmapChunk.
4019 *
4020 * @returns see GMMR0MapUnmapChunk.
4021 * @param pIntNet The internal networking instance.
4022 * @param pSession The caller's session.
4023 * @param pReq The request packet.
4024 */
4025INTNETR0DECL(int) INTNETR0OpenReq(PINTNET pIntNet, PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
4026{
4027 if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
4028 return VERR_INVALID_PARAMETER;
4029 return INTNETR0Open(pIntNet, pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
4030 pReq->fFlags, pReq->cbSend, pReq->cbRecv, &pReq->hIf);
4031}
4032
4033
4034/**
4035 * Destroys an instance of the Ring-0 internal networking service.
4036 *
4037 * @param pIntNet Pointer to the instance data.
4038 */
4039INTNETR0DECL(void) INTNETR0Destroy(PINTNET pIntNet)
4040{
4041 LogFlow(("INTNETR0Destroy: pIntNet=%p\n", pIntNet));
4042
4043 /*
4044 * Allow NULL pointers.
4045 */
4046 if (!pIntNet)
4047 return;
4048 AssertPtrReturnVoid(pIntNet);
4049
4050 /*
4051 * There is not supposed to be any networks hanging around at this time.
4052 */
4053 Assert(pIntNet->pNetworks == NULL);
4054 if (pIntNet->FastMutex != NIL_RTSEMFASTMUTEX)
4055 {
4056 RTSemFastMutexDestroy(pIntNet->FastMutex);
4057 pIntNet->FastMutex = NIL_RTSEMFASTMUTEX;
4058 }
4059 if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
4060 {
4061 /** @todo does it make sense to have a deleter here? */
4062 RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
4063 pIntNet->hHtIfs = NIL_RTHANDLETABLE;
4064 }
4065
4066 RTMemFree(pIntNet);
4067}
4068
4069
4070/**
4071 * Create an instance of the Ring-0 internal networking service.
4072 *
4073 * @returns VBox status code.
4074 * @param ppIntNet Where to store the instance pointer.
4075 */
4076INTNETR0DECL(int) INTNETR0Create(PINTNET *ppIntNet)
4077{
4078 LogFlow(("INTNETR0Create: ppIntNet=%p\n", ppIntNet));
4079 int rc = VERR_NO_MEMORY;
4080 PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
4081 if (pIntNet)
4082 {
4083 //pIntNet->pNetworks = NULL;
4084
4085 rc = RTSemFastMutexCreate(&pIntNet->FastMutex);
4086 if (RT_SUCCESS(rc))
4087 {
4088 rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
4089 UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
4090 if (RT_SUCCESS(rc))
4091 {
4092 *ppIntNet = pIntNet;
4093 LogFlow(("INTNETR0Create: returns VINF_SUCCESS *ppIntNet=%p\n", pIntNet));
4094 return VINF_SUCCESS;
4095 }
4096
4097 RTSemFastMutexDestroy(pIntNet->FastMutex);
4098 }
4099 RTMemFree(pIntNet);
4100 }
4101 *ppIntNet = NULL;
4102 LogFlow(("INTNETR0Create: returns %Rrc\n", rc));
4103 return rc;
4104}
4105
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