VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetFlt/darwin/VBoxNetFlt-darwin.cpp@ 57572

最後變更 在這個檔案從57572是 57358,由 vboxsync 提交於 10 年 前

*: scm cleanup run.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 56.9 KB
 
1/* $Id: VBoxNetFlt-darwin.cpp 57358 2015-08-14 15:16:38Z vboxsync $ */
2/** @file
3 * VBoxNetFlt - Network Filter Driver (Host), Darwin Specific Code.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_NET_FLT_DRV
23#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
24
25#include <VBox/log.h>
26#include <VBox/err.h>
27#include <VBox/intnetinline.h>
28#include <VBox/version.h>
29#include <iprt/initterm.h>
30#include <iprt/assert.h>
31#include <iprt/spinlock.h>
32#include <iprt/semaphore.h>
33#include <iprt/process.h>
34#include <iprt/alloc.h>
35#include <iprt/alloca.h>
36#include <iprt/time.h>
37#include <iprt/net.h>
38#include <iprt/thread.h>
39
40#include "../../darwin/VBoxNetSend.h"
41
42#include <mach/kmod.h>
43#include <sys/conf.h>
44#include <sys/errno.h>
45#include <sys/ioccom.h>
46#include <sys/filio.h>
47#include <sys/malloc.h>
48#include <sys/proc.h>
49#include <sys/socket.h>
50#include <sys/sockio.h>
51#include <sys/kern_event.h>
52#include <net/kpi_interface.h>
53RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
54#include <sys/kpi_mbuf.h>
55#include <net/kpi_interfacefilter.h>
56RT_C_DECLS_END
57
58#include <sys/kpi_socket.h>
59#include <net/if.h>
60#include <net/if_var.h>
61#include <netinet/in.h>
62#include <netinet/in_var.h>
63#include <netinet6/in6_var.h>
64
65#define VBOXNETFLT_OS_SPECFIC 1
66#include "../VBoxNetFltInternal.h"
67
68
69/*********************************************************************************************************************************
70* Defined Constants And Macros *
71*********************************************************************************************************************************/
72/** The maximum number of SG segments.
73 * Used to prevent stack overflow and similar bad stuff. */
74#define VBOXNETFLT_DARWIN_MAX_SEGS 32
75
76#if 0
77/** For testing extremely segmented frames. */
78#define VBOXNETFLT_DARWIN_TEST_SEG_SIZE 14
79#endif
80
81/* XXX: hidden undef #ifdef __APPLE__ */
82#define VBOX_IN_LOOPBACK(addr) (((addr) & IN_CLASSA_NET) == 0x7f000000)
83#define VBOX_IN_LINKLOCAL(addr) (((addr) & IN_CLASSB_NET) == 0xa9fe0000)
84
85
86
87/*********************************************************************************************************************************
88* Internal Functions *
89*********************************************************************************************************************************/
90RT_C_DECLS_BEGIN
91static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData);
92static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData);
93
94static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait);
95RT_C_DECLS_END
96
97
98/*********************************************************************************************************************************
99* Structures and Typedefs *
100*********************************************************************************************************************************/
101/**
102 * The mbuf tag data.
103 *
104 * We have to associate the ethernet header with each packet we're sending
105 * because things like icmp will inherit the tag it self so the tag along
106 * isn't sufficient to identify our mbufs. For the icmp scenario the ethernet
107 * header naturally changes before the packet is send pack, so let check it.
108 */
109typedef struct VBOXNETFLTTAG
110{
111 /** The ethernet header of the outgoing frame. */
112 RTNETETHERHDR EthHdr;
113} VBOXNETFLTTAG;
114/** Pointer to a VBoxNetFlt mbuf tag. */
115typedef VBOXNETFLTTAG *PVBOXNETFLTTAG;
116/** Pointer to a const VBoxNetFlt mbuf tag. */
117typedef VBOXNETFLTTAG const *PCVBOXNETFLTTAG;
118
119
120/*********************************************************************************************************************************
121* Global Variables *
122*********************************************************************************************************************************/
123/**
124 * Declare the module stuff.
125 */
126RT_C_DECLS_BEGIN
127extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
128extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
129
130KMOD_EXPLICIT_DECL(VBoxNetFlt, VBOX_VERSION_STRING, _start, _stop)
131DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxNetFltDarwinStart;
132DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxNetFltDarwinStop;
133DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
134RT_C_DECLS_END
135
136
137/**
138 * The (common) global data.
139 */
140static VBOXNETFLTGLOBALS g_VBoxNetFltGlobals;
141
142/** The unique tag id for this module.
143 * This is basically a unique string hash that lives on until reboot.
144 * It is used for tagging mbufs. */
145static mbuf_tag_id_t g_idTag;
146
147/** The offset of the struct ifnet::if_pcount variable.
148 * @remarks Initial value is valid for Lion and earlier. We adjust it on attach
149 * for later releases. */
150static unsigned g_offIfNetPCount = sizeof(void *) * (1 /*if_softc*/ + 1 /*if_name*/ + 2 /*if_link*/ + 2 /*if_addrhead*/ + 1 /*if_check_multi*/)
151 + sizeof(u_long) /*if_refcnt*/;
152/** Macro for accessing ifnet::if_pcount. */
153#define VBOX_GET_PCOUNT(pIfNet) ( *(int *)((uintptr_t)pIfNet + g_offIfNetPCount) )
154/** The size of area of ifnet structure we try to locate if_pcount in. */
155#define VBOXNETFLT_DARWIN_IFNET_SIZE 256
156/** Indicates whether g_offIfNetPCount has been adjusted already (no point in
157 * doing it more than once). */
158static bool g_fNetPCountFound = false;
159
160
161/**
162 * Change the promiscuous setting and try spot the changed in @a pIfNet.
163 *
164 * @returns Offset of potential p_count field.
165 * @param pIfNet The interface we're attaching to.
166 * @param iPromisc Whether to enable (1) or disable (0) promiscuous mode.
167 *
168 * @note This implementation relies on if_pcount to be aligned on sizeof(int).
169 */
170static unsigned vboxNetFltDarwinSetAndDiff(ifnet_t pIfNet, int iPromisc)
171{
172 int aiSavedState[VBOXNETFLT_DARWIN_IFNET_SIZE / sizeof(int)];
173 memcpy(aiSavedState, pIfNet, sizeof(aiSavedState));
174
175 ifnet_set_promiscuous(pIfNet, iPromisc);
176
177 int const iDiff = iPromisc ? 1 : -1;
178
179 /*
180 * We assume that ifnet structure will never have less members in front of if_pcount
181 * than it used to have in Lion. If this turns out to be false assumption we will
182 * have to start from zero offset.
183 */
184 for (unsigned i = g_offIfNetPCount / sizeof(int); i < RT_ELEMENTS(aiSavedState); i++)
185 if (((int*)pIfNet)[i] - aiSavedState[i] == iDiff)
186 return i * sizeof(int);
187
188 return 0;
189}
190
191
192/**
193 * Detect and adjust the offset of ifnet::if_pcount.
194 *
195 * @param pIfNet The interface we're attaching to.
196 */
197static void vboxNetFltDarwinDetectPCountOffset(ifnet_t pIfNet)
198{
199 if (g_fNetPCountFound)
200 return;
201
202 /*
203 * It would be nice to use locking at this point, but it is not available via KPI.
204 * This is why we try several times. At each attempt we modify if_pcount four times
205 * to rule out false detections.
206 */
207 unsigned offTry1, offTry2, offTry3, offTry4;
208 for (int iAttempt = 0; iAttempt < 3; iAttempt++)
209 {
210 offTry1 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
211 offTry2 = vboxNetFltDarwinSetAndDiff(pIfNet, 1);
212 offTry3 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
213 offTry4 = vboxNetFltDarwinSetAndDiff(pIfNet, 0);
214 if (offTry1 == offTry2 && offTry2 == offTry3 && offTry3 == offTry4)
215 {
216 if (g_offIfNetPCount != offTry1)
217 {
218 Log(("VBoxNetFltDarwinDetectPCountOffset: Adjusted if_pcount offset to %x from %x.\n", offTry1, g_offIfNetPCount));
219 g_offIfNetPCount = offTry1;
220 g_fNetPCountFound = true;
221 }
222 break;
223 }
224 }
225
226 if (g_offIfNetPCount != offTry1)
227 LogRel(("VBoxNetFlt: Failed to detect promiscuous count, all traffic may reach wire (%x != %x).\n", g_offIfNetPCount, offTry1));
228}
229
230
231/**
232 * Start the kernel module.
233 */
234static kern_return_t VBoxNetFltDarwinStart(struct kmod_info *pKModInfo, void *pvData)
235{
236 int rc;
237
238 /*
239 * Initialize IPRT and find our module tag id.
240 * (IPRT is shared with VBoxDrv, it creates the loggers.)
241 */
242 rc = RTR0Init(0);
243 if (RT_SUCCESS(rc))
244 {
245 Log(("VBoxNetFltDarwinStart\n"));
246 errno_t err = mbuf_tag_id_find("org.VirtualBox.kext.VBoxFltDrv", &g_idTag);
247 if (!err)
248 {
249 /*
250 * Initialize the globals and connect to the support driver.
251 *
252 * This will call back vboxNetFltOsOpenSupDrv (and maybe vboxNetFltOsCloseSupDrv)
253 * for establishing the connect to the support driver.
254 */
255 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
256 rc = vboxNetFltInitGlobalsAndIdc(&g_VBoxNetFltGlobals);
257 if (RT_SUCCESS(rc))
258 {
259 LogRel(("VBoxFltDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
260 return KMOD_RETURN_SUCCESS;
261 }
262
263 LogRel(("VBoxFltDrv: failed to initialize device extension (rc=%d)\n", rc));
264 }
265 else
266 LogRel(("VBoxFltDrv: mbuf_tag_id_find failed, err=%d\n", err));
267 RTR0Term();
268 }
269 else
270 printf("VBoxFltDrv: failed to initialize IPRT (rc=%d)\n", rc);
271
272 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
273 return KMOD_RETURN_FAILURE;
274}
275
276
277/**
278 * Stop the kernel module.
279 */
280static kern_return_t VBoxNetFltDarwinStop(struct kmod_info *pKModInfo, void *pvData)
281{
282 Log(("VBoxNetFltDarwinStop\n"));
283
284 /*
285 * Refuse to unload if anyone is currently using the filter driver.
286 * This is important as I/O kit / xnu will to be able to do usage
287 * tracking for us!
288 */
289 int rc = vboxNetFltTryDeleteIdcAndGlobals(&g_VBoxNetFltGlobals);
290 if (RT_FAILURE(rc))
291 {
292 Log(("VBoxNetFltDarwinStop - failed, busy.\n"));
293 return KMOD_RETURN_FAILURE;
294 }
295
296 /*
297 * Undo the work done during start (in reverse order).
298 */
299 memset(&g_VBoxNetFltGlobals, 0, sizeof(g_VBoxNetFltGlobals));
300
301 RTR0Term();
302
303 return KMOD_RETURN_SUCCESS;
304}
305
306
307/**
308 * Reads and retains the host interface handle.
309 *
310 * @returns The handle, NULL if detached.
311 * @param pThis
312 */
313DECLINLINE(ifnet_t) vboxNetFltDarwinRetainIfNet(PVBOXNETFLTINS pThis)
314{
315 ifnet_t pIfNet = NULL;
316
317 /*
318 * Be careful here to avoid problems racing the detached callback.
319 */
320 RTSpinlockAcquire(pThis->hSpinlock);
321 if (!ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost))
322 {
323 pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
324 if (pIfNet)
325 ifnet_reference(pIfNet);
326 }
327 RTSpinlockRelease(pThis->hSpinlock);
328
329 return pIfNet;
330}
331
332
333/**
334 * Release the host interface handle previously retained
335 * by vboxNetFltDarwinRetainIfNet.
336 *
337 * @param pThis The instance.
338 * @param pIfNet The vboxNetFltDarwinRetainIfNet return value, NULL is fine.
339 */
340DECLINLINE(void) vboxNetFltDarwinReleaseIfNet(PVBOXNETFLTINS pThis, ifnet_t pIfNet)
341{
342 NOREF(pThis);
343 if (pIfNet)
344 ifnet_release(pIfNet);
345}
346
347
348/**
349 * Checks whether this is an mbuf created by vboxNetFltDarwinMBufFromSG,
350 * i.e. a buffer which we're pushing and should be ignored by the filter callbacks.
351 *
352 * @returns true / false accordingly.
353 * @param pThis The instance.
354 * @param pMBuf The mbuf.
355 * @param pvFrame The frame pointer, optional.
356 */
357DECLINLINE(bool) vboxNetFltDarwinMBufIsOur(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
358{
359 NOREF(pThis);
360
361 /*
362 * Lookup the tag set by vboxNetFltDarwinMBufFromSG.
363 */
364 PCVBOXNETFLTTAG pTagData;
365 size_t cbTagData;
366 errno_t err = mbuf_tag_find(pMBuf, g_idTag, 0 /* type */, &cbTagData, (void **)&pTagData);
367 if (err)
368 return false;
369 AssertReturn(cbTagData == sizeof(*pTagData), false);
370
371 /*
372 * Dig out the ethernet header from the mbuf.
373 */
374 PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
375 if (!pEthHdr)
376 pEthHdr = (PCRTNETETHERHDR)mbuf_pkthdr_header(pMBuf);
377 if (!pEthHdr)
378 pEthHdr = (PCRTNETETHERHDR)mbuf_data(pMBuf);
379 /* ASSUMING that there is enough data to work on! */
380 if ( pEthHdr->DstMac.au8[0] != pTagData->EthHdr.DstMac.au8[0]
381 || pEthHdr->DstMac.au8[1] != pTagData->EthHdr.DstMac.au8[1]
382 || pEthHdr->DstMac.au8[2] != pTagData->EthHdr.DstMac.au8[2]
383 || pEthHdr->DstMac.au8[3] != pTagData->EthHdr.DstMac.au8[3]
384 || pEthHdr->DstMac.au8[4] != pTagData->EthHdr.DstMac.au8[4]
385 || pEthHdr->DstMac.au8[5] != pTagData->EthHdr.DstMac.au8[5]
386 || pEthHdr->SrcMac.au8[0] != pTagData->EthHdr.SrcMac.au8[0]
387 || pEthHdr->SrcMac.au8[1] != pTagData->EthHdr.SrcMac.au8[1]
388 || pEthHdr->SrcMac.au8[2] != pTagData->EthHdr.SrcMac.au8[2]
389 || pEthHdr->SrcMac.au8[3] != pTagData->EthHdr.SrcMac.au8[3]
390 || pEthHdr->SrcMac.au8[4] != pTagData->EthHdr.SrcMac.au8[4]
391 || pEthHdr->SrcMac.au8[5] != pTagData->EthHdr.SrcMac.au8[5]
392 || pEthHdr->EtherType != pTagData->EthHdr.EtherType)
393 {
394 Log3(("tagged, but the ethernet header has changed\n"));
395 return false;
396 }
397
398 return true;
399}
400
401
402/**
403 * Internal worker that create a darwin mbuf for a (scatter/)gather list.
404 *
405 * @returns Pointer to the mbuf.
406 * @param pThis The instance.
407 * @param pSG The (scatter/)gather list.
408 */
409static mbuf_t vboxNetFltDarwinMBufFromSG(PVBOXNETFLTINS pThis, PINTNETSG pSG)
410{
411 /// @todo future? mbuf_how_t How = preemption enabled ? MBUF_DONTWAIT : MBUF_WAITOK;
412 mbuf_how_t How = MBUF_WAITOK;
413
414 /*
415 * We need some way of getting back to our instance data when
416 * the mbuf is freed, so use pvUserData for this.
417 * -- this is not relevant anylonger! --
418 */
419 Assert(!pSG->pvUserData || pSG->pvUserData == pThis);
420 Assert(!pSG->pvUserData2);
421 pSG->pvUserData = pThis;
422
423 /*
424 * Allocate a packet and copy over the data.
425 *
426 * Using mbuf_attachcluster() here would've been nice but there are two
427 * issues with it: (1) it's 10.5.x only, and (2) the documentation indicates
428 * that it's not supposed to be used for really external buffers. The 2nd
429 * point might be argued against considering that the only m_clattach user
430 * is mallocs memory for the ext mbuf and not doing what's stated in the docs.
431 * However, it's hard to tell if these m_clattach buffers actually makes it
432 * to the NICs or not, and even if they did, the NIC would need the physical
433 * addresses for the pages they contain and might end up copying the data
434 * to a new mbuf anyway.
435 *
436 * So, in the end it's better to just do it the simple way that will work
437 * 100%, even if it involves some extra work (alloc + copy) we really wished
438 * to avoid.
439 *
440 * Note. We can't make use of the physical addresses on darwin because the
441 * way the mbuf / cluster stuff works (see mbuf_data_to_physical and
442 * mcl_to_paddr).
443 */
444 mbuf_t pPkt = NULL;
445 errno_t err = mbuf_allocpacket(How, pSG->cbTotal, NULL, &pPkt);
446 if (!err)
447 {
448 /* Skip zero sized memory buffers (paranoia). */
449 mbuf_t pCur = pPkt;
450 while (pCur && !mbuf_maxlen(pCur))
451 pCur = mbuf_next(pCur);
452 Assert(pCur);
453
454 /* Set the required packet header attributes. */
455 mbuf_pkthdr_setlen(pPkt, pSG->cbTotal);
456 mbuf_pkthdr_setheader(pPkt, mbuf_data(pCur));
457
458 /* Special case the single buffer copy. */
459 if ( mbuf_next(pCur)
460 && mbuf_maxlen(pCur) >= pSG->cbTotal)
461 {
462 mbuf_setlen(pCur, pSG->cbTotal);
463 IntNetSgRead(pSG, mbuf_data(pCur));
464 }
465 else
466 {
467 /* Multi buffer copying. */
468 size_t cbLeft = pSG->cbTotal;
469 size_t offSrc = 0;
470 while (cbLeft > 0 && pCur)
471 {
472 size_t cb = mbuf_maxlen(pCur);
473 if (cb > cbLeft)
474 cb = cbLeft;
475 mbuf_setlen(pCur, cb);
476 IntNetSgReadEx(pSG, offSrc, cb, mbuf_data(pCur));
477
478 /* advance */
479 offSrc += cb;
480 cbLeft -= cb;
481 pCur = mbuf_next(pCur);
482 }
483 Assert(cbLeft == 0);
484 }
485 if (!err)
486 {
487 /*
488 * Tag the packet and return successfully.
489 */
490 PVBOXNETFLTTAG pTagData;
491 err = mbuf_tag_allocate(pPkt, g_idTag, 0 /* type */, sizeof(VBOXNETFLTTAG) /* tag len */, How, (void **)&pTagData);
492 if (!err)
493 {
494 Assert(pSG->aSegs[0].cb >= sizeof(pTagData->EthHdr));
495 memcpy(&pTagData->EthHdr, pSG->aSegs[0].pv, sizeof(pTagData->EthHdr));
496 return pPkt;
497 }
498
499 /* bailout: */
500 AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
501 }
502
503 mbuf_freem(pPkt);
504 }
505 else
506 AssertMsg(err == ENOMEM || err == EWOULDBLOCK, ("err=%d\n", err));
507 pSG->pvUserData = NULL;
508
509 return NULL;
510}
511
512
513/**
514 * Calculates the number of segments required to represent the mbuf.
515 *
516 * @returns Number of segments.
517 * @param pThis The instance.
518 * @param pMBuf The mbuf.
519 * @param pvFrame The frame pointer, optional.
520 */
521DECLINLINE(unsigned) vboxNetFltDarwinMBufCalcSGSegs(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame)
522{
523 NOREF(pThis);
524
525 /*
526 * Count the buffers in the chain.
527 */
528 unsigned cSegs = 0;
529 for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
530 if (mbuf_len(pCur))
531 cSegs++;
532 else if ( !cSegs
533 && pvFrame
534 && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
535 cSegs++;
536
537#ifdef PADD_RUNT_FRAMES_FROM_HOST
538 /*
539 * Add one buffer if the total is less than the ethernet minimum 60 bytes.
540 * This may allocate a segment too much if the ethernet header is separated,
541 * but that shouldn't harm us much.
542 */
543 if (mbuf_pkthdr_len(pMBuf) < 60)
544 cSegs++;
545#endif
546
547#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
548 /* maximize the number of segments. */
549 cSegs = RT_MAX(VBOXNETFLT_DARWIN_MAX_SEGS - 1, cSegs);
550#endif
551
552 return cSegs ? cSegs : 1;
553}
554
555
556/**
557 * Initializes a SG list from an mbuf.
558 *
559 * @returns Number of segments.
560 * @param pThis The instance.
561 * @param pMBuf The mbuf.
562 * @param pSG The SG.
563 * @param pvFrame The frame pointer, optional.
564 * @param cSegs The number of segments allocated for the SG.
565 * This should match the number in the mbuf exactly!
566 * @param fSrc The source of the frame.
567 */
568DECLINLINE(void) vboxNetFltDarwinMBufToSG(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame, PINTNETSG pSG, unsigned cSegs, uint32_t fSrc)
569{
570 NOREF(pThis);
571
572 /*
573 * Walk the chain and convert the buffers to segments. Works INTNETSG::cbTotal.
574 */
575 unsigned iSeg = 0;
576 IntNetSgInitTempSegs(pSG, 0 /*cbTotal*/, cSegs, 0 /*cSegsUsed*/);
577 for (mbuf_t pCur = pMBuf; pCur; pCur = mbuf_next(pCur))
578 {
579 size_t cbSeg = mbuf_len(pCur);
580 if (cbSeg)
581 {
582 void *pvSeg = mbuf_data(pCur);
583
584 /* deal with pvFrame */
585 if (!iSeg && pvFrame && pvFrame != pvSeg)
586 {
587 void *pvStart = mbuf_datastart(pMBuf);
588 uintptr_t offSeg = (uintptr_t)pvSeg - (uintptr_t)pvStart;
589 uintptr_t offSegEnd = offSeg + cbSeg;
590 Assert(pvStart && pvSeg && offSeg < mbuf_maxlen(pMBuf) && offSegEnd <= mbuf_maxlen(pMBuf)); NOREF(offSegEnd);
591 uintptr_t offFrame = (uintptr_t)pvFrame - (uintptr_t)pvStart;
592 if (RT_LIKELY(offFrame < offSeg))
593 {
594 pvSeg = pvFrame;
595 cbSeg += offSeg - offFrame;
596 }
597 else
598 AssertMsgFailed(("pvFrame=%p pvStart=%p pvSeg=%p offSeg=%p cbSeg=%#zx offSegEnd=%p offFrame=%p maxlen=%#zx\n",
599 pvFrame, pvStart, pvSeg, offSeg, cbSeg, offSegEnd, offFrame, mbuf_maxlen(pMBuf)));
600 pvFrame = NULL;
601 }
602
603 AssertBreak(iSeg < cSegs);
604 pSG->cbTotal += cbSeg;
605 pSG->aSegs[iSeg].cb = cbSeg;
606 pSG->aSegs[iSeg].pv = pvSeg;
607 pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
608 iSeg++;
609 }
610 /* The pvFrame might be in a now empty buffer. */
611 else if ( !iSeg
612 && pvFrame
613 && (uintptr_t)pvFrame - (uintptr_t)mbuf_datastart(pMBuf) < mbuf_maxlen(pMBuf))
614 {
615 cbSeg = (uintptr_t)mbuf_datastart(pMBuf) + mbuf_maxlen(pMBuf) - (uintptr_t)pvFrame;
616 pSG->cbTotal += cbSeg;
617 pSG->aSegs[iSeg].cb = cbSeg;
618 pSG->aSegs[iSeg].pv = pvFrame;
619 pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
620 iSeg++;
621 pvFrame = NULL;
622 }
623 }
624
625 Assert(iSeg && iSeg <= cSegs);
626 pSG->cSegsUsed = iSeg;
627
628#ifdef PADD_RUNT_FRAMES_FROM_HOST
629 /*
630 * Add a trailer if the frame is too small.
631 *
632 * Since we're getting to the packet before it is framed, it has not
633 * yet been padded. The current solution is to add a segment pointing
634 * to a buffer containing all zeros and pray that works for all frames...
635 */
636 if (pSG->cbTotal < 60 && (fSrc & INTNETTRUNKDIR_HOST))
637 {
638 AssertReturnVoid(iSeg < cSegs);
639
640 static uint8_t const s_abZero[128] = {0};
641 pSG->aSegs[iSeg].Phys = NIL_RTHCPHYS;
642 pSG->aSegs[iSeg].pv = (void *)&s_abZero[0];
643 pSG->aSegs[iSeg].cb = 60 - pSG->cbTotal;
644 pSG->cbTotal = 60;
645 pSG->cSegsUsed++;
646 }
647#endif
648
649#ifdef VBOXNETFLT_DARWIN_TEST_SEG_SIZE
650 /*
651 * Redistribute the segments.
652 */
653 if (pSG->cSegsUsed < pSG->cSegsAlloc)
654 {
655 /* copy the segments to the end. */
656 int iSrc = pSG->cSegsUsed;
657 int iDst = pSG->cSegsAlloc;
658 while (iSrc > 0)
659 {
660 iDst--;
661 iSrc--;
662 pSG->aSegs[iDst] = pSG->aSegs[iSrc];
663 }
664
665 /* create small segments from the start. */
666 pSG->cSegsUsed = pSG->cSegsAlloc;
667 iSrc = iDst;
668 iDst = 0;
669 while ( iDst < iSrc
670 && iDst < pSG->cSegsAlloc)
671 {
672 pSG->aSegs[iDst].Phys = NIL_RTHCPHYS;
673 pSG->aSegs[iDst].pv = pSG->aSegs[iSrc].pv;
674 pSG->aSegs[iDst].cb = RT_MIN(pSG->aSegs[iSrc].cb, VBOXNETFLT_DARWIN_TEST_SEG_SIZE);
675 if (pSG->aSegs[iDst].cb != pSG->aSegs[iSrc].cb)
676 {
677 pSG->aSegs[iSrc].cb -= pSG->aSegs[iDst].cb;
678 pSG->aSegs[iSrc].pv = (uint8_t *)pSG->aSegs[iSrc].pv + pSG->aSegs[iDst].cb;
679 }
680 else if (++iSrc >= pSG->cSegsAlloc)
681 {
682 pSG->cSegsUsed = iDst + 1;
683 break;
684 }
685 iDst++;
686 }
687 }
688#endif
689
690 AssertMsg(!pvFrame, ("pvFrame=%p pMBuf=%p iSeg=%d\n", pvFrame, pMBuf, iSeg));
691}
692
693
694/**
695 * Helper for determining whether the host wants the interface to be
696 * promiscuous.
697 */
698static bool vboxNetFltDarwinIsPromiscuous(PVBOXNETFLTINS pThis)
699{
700 bool fRc = false;
701 ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
702 if (pIfNet)
703 {
704 /* gather the data */
705 uint16_t fIf = ifnet_flags(pIfNet);
706 unsigned cPromisc = VBOX_GET_PCOUNT(pIfNet);
707 bool fSetPromiscuous = ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous);
708 vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
709
710 /* calc the return. */
711 fRc = (fIf & IFF_PROMISC)
712 && cPromisc > fSetPromiscuous;
713 }
714 return fRc;
715}
716
717
718
719/**
720 *
721 * @see iff_detached_func in the darwin kpi.
722 */
723static void vboxNetFltDarwinIffDetached(void *pvThis, ifnet_t pIfNet)
724{
725 PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
726 uint64_t NanoTS = RTTimeSystemNanoTS();
727 LogFlow(("vboxNetFltDarwinIffDetached: pThis=%p NanoTS=%RU64 (%d)\n",
728 pThis, NanoTS, VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
729
730 Assert(!pThis->fDisconnectedFromHost);
731 Assert(!pThis->fRediscoveryPending);
732
733 /*
734 * If we've put it into promiscuous mode, undo that now. If we don't
735 * the if_pcount will go all wrong when it's replugged.
736 */
737 if (ASMAtomicXchgBool(&pThis->u.s.fSetPromiscuous, false))
738 ifnet_set_promiscuous(pIfNet, 0);
739
740 /*
741 * We carefully take the spinlock and increase the interface reference
742 * behind it in order to avoid problematic races with the detached callback.
743 */
744 RTSpinlockAcquire(pThis->hSpinlock);
745
746 pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
747 int cPromisc = VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : - 1;
748
749 ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfNet);
750 ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
751 ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
752 pThis->u.s.fSetPromiscuous = false;
753 ASMAtomicUoWriteU64(&pThis->NanoTSLastRediscovery, NanoTS);
754 ASMAtomicUoWriteBool(&pThis->fRediscoveryPending, false);
755 ASMAtomicWriteBool(&pThis->fDisconnectedFromHost, true);
756
757 RTSpinlockRelease(pThis->hSpinlock);
758
759 if (pIfNet)
760 ifnet_release(pIfNet);
761 LogRel(("VBoxNetFlt: was detached from '%s' (%d)\n", pThis->szName, cPromisc));
762}
763
764
765/**
766 *
767 * @see iff_ioctl_func in the darwin kpi.
768 */
769static errno_t vboxNetFltDarwinIffIoCtl(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, u_long uCmd, void *pvArg)
770{
771 PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
772 LogFlow(("vboxNetFltDarwinIffIoCtl: pThis=%p uCmd=%lx\n", pThis, uCmd));
773
774 /*
775 * Update fOtherPromiscuous.
776 */
777 /** @todo we'll have to find the offset of if_pcount to get this right! */
778 //if (uCmd == SIOCSIFFLAGS)
779 //{
780 //
781 //}
782
783 /*
784 * We didn't handle it, continue processing.
785 */
786 NOREF(pThis);
787 NOREF(eProtocol);
788 NOREF(uCmd);
789 NOREF(pvArg);
790 return EOPNOTSUPP;
791}
792
793
794/**
795 *
796 * @see iff_event_func in the darwin kpi.
797 */
798static void vboxNetFltDarwinIffEvent(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, const struct kev_msg *pEvMsg)
799{
800 PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvThis;
801 LogFlow(("vboxNetFltDarwinIffEvent: pThis=%p\n", pThis));
802
803 NOREF(pThis);
804 NOREF(pIfNet);
805 NOREF(eProtocol);
806 NOREF(pEvMsg);
807
808 /*
809 * Watch out for the interface going online / offline.
810 */
811 if ( VALID_PTR(pThis)
812 && VALID_PTR(pEvMsg)
813 && pEvMsg->vendor_code == KEV_VENDOR_APPLE
814 && pEvMsg->kev_class == KEV_NETWORK_CLASS
815 && pEvMsg->kev_subclass == KEV_DL_SUBCLASS)
816 {
817 if (pThis->u.s.pIfNet == pIfNet)
818 {
819 if (pEvMsg->event_code == KEV_DL_LINK_ON)
820 {
821 if (ASMAtomicUoReadBool(&pThis->u.s.fNeedSetPromiscuous))
822 {
823 /* failed to bring it online. */
824 errno_t err = ifnet_set_promiscuous(pIfNet, 1);
825 if (!err)
826 {
827 ASMAtomicWriteBool(&pThis->u.s.fSetPromiscuous, true);
828 ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
829 Log(("vboxNetFltDarwinIffEvent: enabled promiscuous mode on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
830 }
831 else
832 Log(("vboxNetFltDarwinIffEvent: ifnet_set_promiscuous failed on %s, err=%d (%d)\n", pThis->szName, err, VBOX_GET_PCOUNT(pIfNet)));
833 }
834 else if ( ASMAtomicUoReadBool(&pThis->u.s.fSetPromiscuous)
835 && !(ifnet_flags(pIfNet) & IFF_PROMISC))
836 {
837 /* Try fix the inconsistency. */
838 errno_t err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
839 if (!err)
840 err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
841 if (!err && (ifnet_flags(pIfNet) & IFF_PROMISC))
842 Log(("vboxNetFltDarwinIffEvent: fixed IFF_PROMISC on %s (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
843 else
844 Log(("vboxNetFltDarwinIffEvent: failed to fix IFF_PROMISC on %s, err=%d flags=%#x (%d)\n",
845 pThis->szName, err, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
846 }
847 else
848 Log(("vboxNetFltDarwinIffEvent: online, '%s'. flags=%#x (%d)\n", pThis->szName, ifnet_flags(pIfNet), VBOX_GET_PCOUNT(pIfNet)));
849 }
850 else if (pEvMsg->event_code == KEV_DL_LINK_OFF)
851 Log(("vboxNetFltDarwinIffEvent: %s goes down (%d)\n", pThis->szName, VBOX_GET_PCOUNT(pIfNet)));
852/** @todo KEV_DL_LINK_ADDRESS_CHANGED -> pfnReportMacAddress */
853/** @todo KEV_DL_SIFFLAGS -> pfnReportPromiscuousMode */
854 }
855 else
856 Log(("vboxNetFltDarwinIffEvent: pThis->u.s.pIfNet=%p pIfNet=%p (%d)\n", pThis->u.s.pIfNet, pIfNet, VALID_PTR(pIfNet) ? VBOX_GET_PCOUNT(pIfNet) : -1));
857 }
858 else if (VALID_PTR(pEvMsg))
859 Log(("vboxNetFltDarwinIffEvent: vendor_code=%#x kev_class=%#x kev_subclass=%#x event_code=%#x\n",
860 pEvMsg->vendor_code, pEvMsg->kev_class, pEvMsg->kev_subclass, pEvMsg->event_code));
861}
862
863
864/**
865 * Internal worker for vboxNetFltDarwinIffInput and vboxNetFltDarwinIffOutput,
866 *
867 * @returns 0 or EJUSTRETURN.
868 * @param pThis The instance.
869 * @param pMBuf The mbuf.
870 * @param pvFrame The start of the frame, optional.
871 * @param fSrc Where the packet (allegedly) comes from, one INTNETTRUNKDIR_* value.
872 * @param eProtocol The protocol.
873 */
874static errno_t vboxNetFltDarwinIffInputOutputWorker(PVBOXNETFLTINS pThis, mbuf_t pMBuf, void *pvFrame,
875 uint32_t fSrc, protocol_family_t eProtocol)
876{
877 /*
878 * Drop it immediately?
879 */
880 Log2(("vboxNetFltDarwinIffInputOutputWorker: pThis=%p pMBuf=%p pvFrame=%p fSrc=%#x cbPkt=%x\n",
881 pThis, pMBuf, pvFrame, fSrc, pMBuf ? mbuf_pkthdr_len(pMBuf) : -1));
882 if (!pMBuf)
883 return 0;
884#if 0 /* debugging lost icmp packets */
885 if (mbuf_pkthdr_len(pMBuf) > 0x300)
886 {
887 uint8_t *pb = (uint8_t *)(pvFrame ? pvFrame : mbuf_data(pMBuf));
888 Log3(("D=%.6Rhxs S=%.6Rhxs T=%04x IFF\n", pb, pb + 6, RT_BE2H_U16(*(uint16_t *)(pb + 12))));
889 }
890#endif
891 if (vboxNetFltDarwinMBufIsOur(pThis, pMBuf, pvFrame))
892 return 0;
893
894 /*
895 * Active? Retain the instance and increment the busy counter.
896 */
897 if (!vboxNetFltTryRetainBusyActive(pThis))
898 return 0;
899
900 /*
901 * Finalize out-bound packets since the stack puts off finalizing
902 * TCP/IP checksums as long as possible.
903 * ASSUMES this only applies to outbound IP packets.
904 */
905 if ( (fSrc & INTNETTRUNKDIR_HOST)
906 && eProtocol == PF_INET)
907 {
908 Assert(!pvFrame);
909 mbuf_outbound_finalize(pMBuf, eProtocol, sizeof(RTNETETHERHDR));
910 }
911
912 /*
913 * Create a (scatter/)gather list for the mbuf and feed it to the internal network.
914 */
915 bool fDropIt = false;
916 unsigned cSegs = vboxNetFltDarwinMBufCalcSGSegs(pThis, pMBuf, pvFrame);
917 if (cSegs < VBOXNETFLT_DARWIN_MAX_SEGS)
918 {
919 PINTNETSG pSG = (PINTNETSG)alloca(RT_OFFSETOF(INTNETSG, aSegs[cSegs]));
920 vboxNetFltDarwinMBufToSG(pThis, pMBuf, pvFrame, pSG, cSegs, fSrc);
921
922 fDropIt = pThis->pSwitchPort->pfnRecv(pThis->pSwitchPort, NULL /* pvIf */, pSG, fSrc);
923 if (fDropIt)
924 {
925 /*
926 * Check if this interface is in promiscuous mode. We should not drop
927 * any packets before they get to the driver as it passes them to tap
928 * callbacks in order for BPF to work properly.
929 */
930 if (vboxNetFltDarwinIsPromiscuous(pThis))
931 fDropIt = false;
932 else
933 mbuf_freem(pMBuf);
934 }
935 }
936
937 vboxNetFltRelease(pThis, true /* fBusy */);
938
939 return fDropIt ? EJUSTRETURN : 0;
940}
941
942
943/**
944 * From the host.
945 *
946 * @see iff_output_func in the darwin kpi.
947 */
948static errno_t vboxNetFltDarwinIffOutput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf)
949{
950 /** @todo there was some note about the ethernet header here or something like that... */
951
952 NOREF(eProtocol);
953 NOREF(pIfNet);
954 return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, NULL, INTNETTRUNKDIR_HOST, eProtocol);
955}
956
957
958/**
959 * From the wire.
960 *
961 * @see iff_input_func in the darwin kpi.
962 */
963static errno_t vboxNetFltDarwinIffInput(void *pvThis, ifnet_t pIfNet, protocol_family_t eProtocol, mbuf_t *ppMBuf, char **ppchFrame)
964{
965 NOREF(eProtocol);
966 NOREF(pIfNet);
967 return vboxNetFltDarwinIffInputOutputWorker((PVBOXNETFLTINS)pvThis, *ppMBuf, *ppchFrame, INTNETTRUNKDIR_WIRE, eProtocol);
968}
969
970
971/** A worker thread for vboxNetFltSendDummy(). */
972static DECLCALLBACK(int) vboxNetFltSendDummyWorker(RTTHREAD hThreadSelf, void *pvUser)
973{
974 Assert(pvUser);
975 ifnet_t pIfNet = (ifnet_t)pvUser;
976 return VBoxNetSendDummy(pIfNet);
977}
978
979
980/**
981 * Prevent GUI icon freeze issue when VirtualBoxVM process terminates.
982 *
983 * This function is a workaround for stuck-in-dock issue. The idea here is to
984 * send a dummy packet to an interface from the context of a kernel thread.
985 * Therefore, an XNU's receive thread (which is created as a result if we are
986 * the first who is communicating with the interface) will be associated with
987 * the kernel thread instead of VirtualBoxVM process.
988 *
989 * @param pIfNet Interface to be used to send data.
990 */
991static void vboxNetFltSendDummy(ifnet_t pIfNet)
992{
993 RTTHREAD hThread;
994 int rc = RTThreadCreate(&hThread, vboxNetFltSendDummyWorker, (void *)pIfNet, 0,
995 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "DummyThread");
996 if (RT_SUCCESS(rc))
997 {
998 RTThreadWait(hThread, RT_INDEFINITE_WAIT, NULL);
999 LogFlow(("vboxNetFltSendDummy: a dummy packet has been successfully sent in order to prevent stuck-in-dock issue\n"));
1000 }
1001 else
1002 LogFlow(("vboxNetFltSendDummy: unable to send dummy packet in order to prevent stuck-in-dock issue\n"));
1003}
1004
1005
1006/**
1007 * Internal worker for vboxNetFltOsInitInstance and vboxNetFltOsMaybeRediscovered.
1008 *
1009 * @returns VBox status code.
1010 * @param pThis The instance.
1011 * @param fRediscovery If set we're doing a rediscovery attempt, so, don't
1012 * flood the release log.
1013 */
1014static int vboxNetFltDarwinAttachToInterface(PVBOXNETFLTINS pThis, bool fRediscovery)
1015{
1016 LogFlow(("vboxNetFltDarwinAttachToInterface: pThis=%p (%s)\n", pThis, pThis->szName));
1017 IPRT_DARWIN_SAVE_EFL_AC();
1018
1019 /*
1020 * Locate the interface first.
1021 *
1022 * The pIfNet member is updated before iflt_attach is called and used
1023 * to deal with the hypothetical case where someone rips out the
1024 * interface immediately after our iflt_attach call.
1025 */
1026 ifnet_t pIfNet = NULL;
1027 errno_t err = ifnet_find_by_name(pThis->szName, &pIfNet);
1028 if (err)
1029 {
1030 Assert(err == ENXIO);
1031 if (!fRediscovery)
1032 LogRel(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
1033 else
1034 Log(("VBoxFltDrv: failed to find ifnet '%s' (err=%d)\n", pThis->szName, err));
1035 IPRT_DARWIN_RESTORE_EFL_AC();
1036 return VERR_INTNET_FLT_IF_NOT_FOUND;
1037 }
1038
1039 RTSpinlockAcquire(pThis->hSpinlock);
1040 ASMAtomicUoWritePtr(&pThis->u.s.pIfNet, pIfNet);
1041 RTSpinlockRelease(pThis->hSpinlock);
1042
1043 /* Adjust g_offIfNetPCount as it varies for different versions of xnu. */
1044 vboxNetFltDarwinDetectPCountOffset(pIfNet);
1045
1046 /* Prevent stuck-in-dock issue by associating interface receive thread with kernel thread. */
1047 vboxNetFltSendDummy(pIfNet);
1048
1049 /*
1050 * Get the mac address while we still have a valid ifnet reference.
1051 */
1052 err = ifnet_lladdr_copy_bytes(pIfNet, &pThis->u.s.MacAddr, sizeof(pThis->u.s.MacAddr));
1053 if (!err)
1054 {
1055 /*
1056 * Try attach the filter.
1057 */
1058 struct iff_filter RegRec;
1059 RegRec.iff_cookie = pThis;
1060 RegRec.iff_name = "VBoxNetFlt";
1061 RegRec.iff_protocol = 0;
1062 RegRec.iff_input = vboxNetFltDarwinIffInput;
1063 RegRec.iff_output = vboxNetFltDarwinIffOutput;
1064 RegRec.iff_event = vboxNetFltDarwinIffEvent;
1065 RegRec.iff_ioctl = vboxNetFltDarwinIffIoCtl;
1066 RegRec.iff_detached = vboxNetFltDarwinIffDetached;
1067 interface_filter_t pIfFilter = NULL;
1068 err = iflt_attach(pIfNet, &RegRec, &pIfFilter);
1069 Assert(err || pIfFilter);
1070
1071 RTSpinlockAcquire(pThis->hSpinlock);
1072 pIfNet = ASMAtomicUoReadPtrT(&pThis->u.s.pIfNet, ifnet_t);
1073 if (pIfNet && !err)
1074 {
1075 ASMAtomicUoWriteBool(&pThis->fDisconnectedFromHost, false);
1076 ASMAtomicUoWritePtr(&pThis->u.s.pIfFilter, pIfFilter);
1077 pIfNet = NULL; /* don't dereference it */
1078 }
1079 RTSpinlockRelease(pThis->hSpinlock);
1080
1081 /* Report capabilities. */
1082 if ( !pIfNet
1083 && vboxNetFltTryRetainBusyNotDisconnected(pThis))
1084 {
1085 Assert(pThis->pSwitchPort);
1086 pThis->pSwitchPort->pfnReportMacAddress(pThis->pSwitchPort, &pThis->u.s.MacAddr);
1087 pThis->pSwitchPort->pfnReportPromiscuousMode(pThis->pSwitchPort, vboxNetFltDarwinIsPromiscuous(pThis));
1088 pThis->pSwitchPort->pfnReportGsoCapabilities(pThis->pSwitchPort, 0, INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST);
1089 pThis->pSwitchPort->pfnReportNoPreemptDsts(pThis->pSwitchPort, 0 /* none */);
1090 vboxNetFltRelease(pThis, true /*fBusy*/);
1091 }
1092 }
1093
1094 /* Release the interface on failure. */
1095 if (pIfNet)
1096 ifnet_release(pIfNet);
1097
1098 int rc = RTErrConvertFromErrno(err);
1099 if (RT_SUCCESS(rc))
1100 LogRel(("VBoxFltDrv: attached to '%s' / %RTmac\n", pThis->szName, &pThis->u.s.MacAddr));
1101 else
1102 LogRel(("VBoxFltDrv: failed to attach to ifnet '%s' (err=%d)\n", pThis->szName, err));
1103 IPRT_DARWIN_RESTORE_EFL_AC();
1104 return rc;
1105}
1106
1107
1108bool vboxNetFltOsMaybeRediscovered(PVBOXNETFLTINS pThis)
1109{
1110 vboxNetFltDarwinAttachToInterface(pThis, true /* fRediscovery */);
1111 return !ASMAtomicUoReadBool(&pThis->fDisconnectedFromHost);
1112}
1113
1114
1115int vboxNetFltPortOsXmit(PVBOXNETFLTINS pThis, void *pvIfData, PINTNETSG pSG, uint32_t fDst)
1116{
1117 IPRT_DARWIN_SAVE_EFL_AC();
1118 NOREF(pvIfData);
1119
1120 int rc = VINF_SUCCESS;
1121 ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
1122 if (pIfNet)
1123 {
1124 /*
1125 * Create a mbuf for the gather list and push it onto the wire.
1126 *
1127 * Note! If the interface is in the promiscuous mode we need to send the
1128 * packet down the stack so it reaches the driver and Berkeley
1129 * Packet Filter (see @bugref{5817}).
1130 */
1131 if ((fDst & INTNETTRUNKDIR_WIRE) || vboxNetFltDarwinIsPromiscuous(pThis))
1132 {
1133 mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
1134 if (pMBuf)
1135 {
1136 errno_t err = ifnet_output_raw(pIfNet, PF_LINK, pMBuf);
1137 if (err)
1138 rc = RTErrConvertFromErrno(err);
1139 }
1140 else
1141 rc = VERR_NO_MEMORY;
1142 }
1143
1144 /*
1145 * Create a mbuf for the gather list and push it onto the host stack.
1146 */
1147 if (fDst & INTNETTRUNKDIR_HOST)
1148 {
1149 mbuf_t pMBuf = vboxNetFltDarwinMBufFromSG(pThis, pSG);
1150 if (pMBuf)
1151 {
1152 /* This is what IONetworkInterface::inputPacket does. */
1153 unsigned const cbEthHdr = 14;
1154 mbuf_pkthdr_setheader(pMBuf, mbuf_data(pMBuf));
1155 mbuf_pkthdr_setlen(pMBuf, mbuf_pkthdr_len(pMBuf) - cbEthHdr);
1156 mbuf_setdata(pMBuf, (uint8_t *)mbuf_data(pMBuf) + cbEthHdr, mbuf_len(pMBuf) - cbEthHdr);
1157 mbuf_pkthdr_setrcvif(pMBuf, pIfNet); /* will crash without this. */
1158
1159 errno_t err = ifnet_input(pIfNet, pMBuf, NULL);
1160 if (err)
1161 rc = RTErrConvertFromErrno(err);
1162 }
1163 else
1164 rc = VERR_NO_MEMORY;
1165 }
1166
1167 vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
1168 }
1169
1170 IPRT_DARWIN_RESTORE_EFL_AC();
1171 return rc;
1172}
1173
1174
1175void vboxNetFltPortOsSetActive(PVBOXNETFLTINS pThis, bool fActive)
1176{
1177 IPRT_DARWIN_SAVE_EFL_AC();
1178 ifnet_t pIfNet = vboxNetFltDarwinRetainIfNet(pThis);
1179 if (pIfNet)
1180 {
1181 if (pThis->fDisablePromiscuous)
1182 {
1183 /*
1184 * Promiscuous mode should not be used (wireless), we just need to
1185 * make sure the interface is up.
1186 */
1187 if (fActive)
1188 {
1189 u_int16_t fIf = ifnet_flags(pIfNet);
1190 if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
1191 {
1192 ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
1193 ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
1194 }
1195 }
1196 }
1197 else
1198 {
1199 /*
1200 * This api is a bit weird, the best reference is the code.
1201 *
1202 * Also, we have a bit or race conditions wrt the maintenance of
1203 * host the interface promiscuity for vboxNetFltPortOsIsPromiscuous.
1204 */
1205 unsigned const cPromiscBefore = VBOX_GET_PCOUNT(pIfNet);
1206 u_int16_t fIf;
1207 if (fActive)
1208 {
1209 Assert(!pThis->u.s.fSetPromiscuous);
1210 errno_t err = ENETDOWN;
1211 ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, true);
1212
1213 /*
1214 * Try bring the interface up and running if it's down.
1215 */
1216 fIf = ifnet_flags(pIfNet);
1217 if ((fIf & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
1218 {
1219 err = ifnet_set_flags(pIfNet, IFF_UP, IFF_UP);
1220 errno_t err2 = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
1221 if (!err)
1222 err = err2;
1223 fIf = ifnet_flags(pIfNet);
1224 }
1225
1226 /*
1227 * Is it already up? If it isn't, leave it to the link event or
1228 * we'll upset if_pcount (as stated above, ifnet_set_promiscuous is weird).
1229 */
1230 if ((fIf & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
1231 {
1232 err = ifnet_set_promiscuous(pIfNet, 1);
1233 pThis->u.s.fSetPromiscuous = err == 0;
1234 if (!err)
1235 {
1236 ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
1237
1238 /* check if it actually worked, this stuff is not always behaving well. */
1239 if (!(ifnet_flags(pIfNet) & IFF_PROMISC))
1240 {
1241 err = ifnet_set_flags(pIfNet, IFF_PROMISC, IFF_PROMISC);
1242 if (!err)
1243 err = ifnet_ioctl(pIfNet, 0, SIOCSIFFLAGS, NULL);
1244 if (!err)
1245 Log(("vboxNetFlt: fixed IFF_PROMISC on %s (%d->%d)\n", pThis->szName, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1246 else
1247 Log(("VBoxNetFlt: failed to fix IFF_PROMISC on %s, err=%d (%d->%d)\n",
1248 pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1249 }
1250 }
1251 else
1252 Log(("VBoxNetFlt: ifnet_set_promiscuous -> err=%d grr! (%d->%d)\n", err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1253 }
1254 else if (!err)
1255 Log(("VBoxNetFlt: Waiting for the link to come up... (%d->%d)\n", cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1256 if (err)
1257 LogRel(("VBoxNetFlt: Failed to put '%s' into promiscuous mode, err=%d (%d->%d)\n", pThis->szName, err, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1258 }
1259 else
1260 {
1261 ASMAtomicWriteBool(&pThis->u.s.fNeedSetPromiscuous, false);
1262 if (pThis->u.s.fSetPromiscuous)
1263 {
1264 errno_t err = ifnet_set_promiscuous(pIfNet, 0);
1265 AssertMsg(!err, ("%d\n", err)); NOREF(err);
1266 }
1267 pThis->u.s.fSetPromiscuous = false;
1268
1269 fIf = ifnet_flags(pIfNet);
1270 Log(("VBoxNetFlt: fIf=%#x; %d->%d\n", fIf, cPromiscBefore, VBOX_GET_PCOUNT(pIfNet)));
1271 }
1272 }
1273
1274 vboxNetFltDarwinReleaseIfNet(pThis, pIfNet);
1275 }
1276 IPRT_DARWIN_RESTORE_EFL_AC();
1277}
1278
1279
1280int vboxNetFltOsDisconnectIt(PVBOXNETFLTINS pThis)
1281{
1282 /* Nothing to do here. */
1283 return VINF_SUCCESS;
1284}
1285
1286
1287int vboxNetFltOsConnectIt(PVBOXNETFLTINS pThis)
1288{
1289 /* Nothing to do here. */
1290 return VINF_SUCCESS;
1291}
1292
1293
1294void vboxNetFltOsDeleteInstance(PVBOXNETFLTINS pThis)
1295{
1296 IPRT_DARWIN_SAVE_EFL_AC();
1297
1298 /*
1299 * Carefully obtain the interface filter reference and detach it.
1300 */
1301 RTSpinlockAcquire(pThis->hSpinlock);
1302 interface_filter_t pIfFilter = ASMAtomicUoReadPtrT(&pThis->u.s.pIfFilter, interface_filter_t);
1303 if (pIfFilter)
1304 ASMAtomicUoWriteNullPtr(&pThis->u.s.pIfFilter);
1305 RTSpinlockRelease(pThis->hSpinlock);
1306
1307 if (pIfFilter)
1308 iflt_detach(pIfFilter);
1309
1310 if (pThis->u.s.pSysSock != NULL)
1311 {
1312 sock_close(pThis->u.s.pSysSock);
1313 pThis->u.s.pSysSock = NULL;
1314 }
1315
1316 IPRT_DARWIN_RESTORE_EFL_AC();
1317}
1318
1319
1320int vboxNetFltOsInitInstance(PVBOXNETFLTINS pThis, void *pvContext)
1321{
1322 NOREF(pvContext);
1323
1324 int rc = vboxNetFltDarwinAttachToInterface(pThis, false /* fRediscovery */);
1325 if (RT_FAILURE(rc))
1326 return rc;
1327
1328 if (pThis->pSwitchPort->pfnNotifyHostAddress == NULL)
1329 return rc;
1330
1331 /*
1332 * XXX: uwe
1333 *
1334 * Learn host's IP addresses and set up notifications for changes.
1335 * To avoid racing, set up notifications first.
1336 *
1337 * XXX: This should probably be global, since the only thing
1338 * specific to ifnet here is its IPv6 link-local address.
1339 */
1340 IPRT_DARWIN_SAVE_EFL_AC();
1341 errno_t error;
1342
1343 /** @todo reorg code to not have numerous returns with duplicate code... */
1344 error = sock_socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT,
1345 vboxNetFltDarwinSysSockUpcall, pThis,
1346 &pThis->u.s.pSysSock);
1347 if (error != 0)
1348 {
1349 LogRel(("sock_socket(SYSPROTO_EVENT): error %d\n", error));
1350 IPRT_DARWIN_RESTORE_EFL_AC();
1351 return rc;
1352 }
1353
1354 int nbio = 1;
1355 error = sock_ioctl(pThis->u.s.pSysSock, FIONBIO, &nbio);
1356 if (error != 0)
1357 {
1358 LogRel(("FIONBIO: error %d\n", error));
1359 sock_close(pThis->u.s.pSysSock);
1360 IPRT_DARWIN_RESTORE_EFL_AC();
1361 return rc;
1362 }
1363
1364 if (!sock_isnonblocking(pThis->u.s.pSysSock))
1365 {
1366 LogRel(("FIONBIO ok, but socket is blocking?!\n"));
1367 sock_close(pThis->u.s.pSysSock);
1368 IPRT_DARWIN_RESTORE_EFL_AC();
1369 return rc;
1370 }
1371
1372 struct kev_request req;
1373 req.vendor_code = KEV_VENDOR_APPLE;
1374 req.kev_class = KEV_NETWORK_CLASS;
1375 req.kev_subclass = KEV_ANY_SUBCLASS; /* need both INET and INET6, so have to request all */
1376
1377 error = sock_ioctl(pThis->u.s.pSysSock, SIOCSKEVFILT, &req);
1378 if (error != 0)
1379 {
1380 LogRel(("SIOCSKEVFILT: error %d\n", error));
1381 sock_close(pThis->u.s.pSysSock);
1382 IPRT_DARWIN_RESTORE_EFL_AC();
1383 return rc;
1384 }
1385
1386 ifnet_t pIfNet = pThis->u.s.pIfNet; /* already retained */
1387
1388 ifaddr_t *pIfAddrList;
1389 error = ifnet_get_address_list(/* all interfaces*/ NULL, &pIfAddrList);
1390 if (error != 0)
1391 {
1392 LogRel(("ifnet_get_address_list: error %d\n", error));
1393 IPRT_DARWIN_RESTORE_EFL_AC();
1394 return rc;
1395 }
1396
1397 for (ifaddr_t *pIfAddr = pIfAddrList; *pIfAddr != NULL; ++pIfAddr)
1398 {
1399 ifaddr_t ifa = *pIfAddr;
1400 sa_family_t family = ifaddr_address_family(ifa);
1401 struct sockaddr_storage ss;
1402
1403 error = ifaddr_address(ifa, (struct sockaddr *)&ss, sizeof(ss));
1404 if (error != 0)
1405 {
1406 LogRel(("getting address family %d: error %d\n", family, error));
1407 continue;
1408 }
1409
1410 if (family == AF_INET)
1411 {
1412 struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
1413 u_int32_t u32Addr = ntohl(sin->sin_addr.s_addr);
1414
1415 if (VBOX_IN_LOOPBACK(u32Addr))
1416 continue;
1417
1418 if (ifaddr_ifnet(ifa) != pIfNet && VBOX_IN_LINKLOCAL(u32Addr))
1419 continue;
1420
1421 Log(("> inet %RTnaipv4\n", sin->sin_addr.s_addr));
1422 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
1423 /* :fAdded */ true, kIntNetAddrType_IPv4, &sin->sin_addr);
1424 }
1425 else if (family == AF_INET6)
1426 {
1427 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
1428
1429 if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
1430 continue;
1431
1432 /* link-local from other interfaces are out of scope */
1433 if (ifaddr_ifnet(ifa) != pIfNet && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
1434 continue;
1435
1436 Log(("> inet6 %RTnaipv6\n", &sin6->sin6_addr));
1437 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
1438 /* :fAdded */ true, kIntNetAddrType_IPv6, &sin6->sin6_addr);
1439 }
1440 }
1441
1442 ifnet_free_address_list(pIfAddrList);
1443
1444 /*
1445 * Now that we've got current addresses, check for events that
1446 * might have happened while we were working.
1447 */
1448 vboxNetFltDarwinSysSockUpcall(pThis->u.s.pSysSock, pThis, MBUF_DONTWAIT);
1449
1450 IPRT_DARWIN_RESTORE_EFL_AC();
1451 return rc;
1452}
1453
1454
1455static void vboxNetFltDarwinSysSockUpcall(socket_t pSysSock, void *pvData, int fWait)
1456{
1457 PVBOXNETFLTINS pThis = (PVBOXNETFLTINS)pvData;
1458 errno_t error;
1459
1460 NOREF(fWait);
1461
1462 if (RT_UNLIKELY(pSysSock != pThis->u.s.pSysSock))
1463 {
1464 Log(("vboxNetFltDarwinSysSockUpcall: %p != %p?\n",
1465 pSysSock, pThis->u.s.pSysSock));
1466 return;
1467 }
1468
1469 struct net_event_data my_link;
1470 ifnet_t pIfNet = pThis->u.s.pIfNet; /* XXX: retain? */
1471 ifnet_family_t if_family = ifnet_family(pIfNet);
1472 u_int32_t if_unit = ifnet_unit(pIfNet);
1473
1474 for (;;)
1475 {
1476 mbuf_t m;
1477 size_t len = sizeof(struct kern_event_msg) - sizeof(u_int32_t) + sizeof(struct kev_in6_data);
1478
1479 error = sock_receivembuf(pSysSock, NULL, &m, 0, &len);
1480 if (error != 0)
1481 {
1482 if (error == EWOULDBLOCK)
1483 {
1484 Log(("vboxNetFltDarwinSysSockUpcall: EWOULDBLOCK - we are done\n"));
1485 error = 0;
1486 }
1487 else
1488 Log(("sock_receivembuf: error %d\n", error));
1489 break;
1490 }
1491
1492 if (len < sizeof(struct kern_event_msg) - sizeof(u_int32_t))
1493 {
1494 Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short\n", (unsigned int)len));
1495 mbuf_freem(m);
1496 return;
1497 }
1498
1499 struct kern_event_msg *msg = (struct kern_event_msg *)mbuf_data(m);
1500 if (msg->kev_subclass == KEV_INET_SUBCLASS)
1501 {
1502 if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in_data))
1503 {
1504 Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET_SUBCLASS\n",
1505 (unsigned int)len));
1506 mbuf_freem(m);
1507 return;
1508 }
1509
1510 struct kev_in_data *iev = (struct kev_in_data *)msg->event_data;
1511 struct net_event_data *link = &iev->link_data;
1512 PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev->ia_addr;
1513 u_int32_t u32Addr = ntohl(pAddr->IPv4.u);
1514
1515 if (VBOX_IN_LOOPBACK(u32Addr))
1516 {
1517 mbuf_freem(m);
1518 continue;
1519 }
1520
1521 if ( (link->if_family != if_family || link->if_unit != if_unit)
1522 && VBOX_IN_LINKLOCAL(u32Addr))
1523 {
1524 mbuf_freem(m);
1525 continue;
1526 }
1527
1528 switch (msg->event_code)
1529 {
1530 case KEV_INET_NEW_ADDR:
1531 Log(("KEV_INET_NEW_ADDR %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
1532 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, true /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
1533 break;
1534
1535 case KEV_INET_ADDR_DELETED:
1536 Log(("KEV_INET_ADDR_DELETED %.*s%d: %RTnaipv4\n", IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
1537 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort, false /*fAdded*/, kIntNetAddrType_IPv4, pAddr);
1538 break;
1539
1540 default:
1541 Log(("KEV INET event %u %.*s%d: addr %RTnaipv4\n",
1542 msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr->IPv4.u));
1543 break;
1544 }
1545 }
1546 else if (msg->kev_subclass == KEV_INET6_SUBCLASS)
1547 {
1548 if (len - (sizeof(struct kern_event_msg) - sizeof(u_int32_t)) < sizeof(struct kev_in6_data))
1549 {
1550 Log(("vboxNetFltDarwinSysSockUpcall: %u bytes is too short for KEV_INET6_SUBCLASS\n",
1551 (unsigned int)len));
1552 mbuf_freem(m);
1553 return;
1554 }
1555
1556 struct kev_in6_data *iev6 = (struct kev_in6_data *)msg->event_data;
1557 struct net_event_data *link = &iev6->link_data;
1558 PCRTNETADDRU pAddr = (PCRTNETADDRU)&iev6->ia_addr.sin6_addr;
1559
1560 if (IN6_IS_ADDR_LOOPBACK(&iev6->ia_addr.sin6_addr))
1561 {
1562 mbuf_freem(m);
1563 continue;
1564 }
1565
1566 if ( (link->if_family != if_family || link->if_unit != if_unit)
1567 && IN6_IS_ADDR_LINKLOCAL(&iev6->ia_addr.sin6_addr))
1568 {
1569 mbuf_freem(m);
1570 continue;
1571 }
1572
1573 switch (msg->event_code)
1574 {
1575 case KEV_INET6_NEW_USER_ADDR:
1576 Log(("KEV_INET6_NEW_USER_ADDR %.*s%d: %RTnaipv6\n",
1577 IFNAMSIZ, link->if_name, link->if_unit, pAddr));
1578 goto kev_inet6_new;
1579
1580 case KEV_INET6_NEW_LL_ADDR:
1581 Log(("KEV_INET6_NEW_LL_ADDR %.*s%d: %RTnaipv6\n",
1582 IFNAMSIZ, link->if_name, link->if_unit, pAddr));
1583 goto kev_inet6_new;
1584
1585 case KEV_INET6_NEW_RTADV_ADDR:
1586 Log(("KEV_INET6_NEW_RTADV_ADDR %.*s%d: %RTnaipv6\n",
1587 IFNAMSIZ, link->if_name, link->if_unit, pAddr));
1588 goto kev_inet6_new;
1589
1590 kev_inet6_new:
1591 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
1592 /* :fAdded */ true, kIntNetAddrType_IPv6, pAddr);
1593 break;
1594
1595 case KEV_INET6_ADDR_DELETED:
1596 Log(("KEV_INET6_ADDR_DELETED %.*s%d: %RTnaipv6\n",
1597 IFNAMSIZ, link->if_name, link->if_unit, pAddr));
1598
1599 pThis->pSwitchPort->pfnNotifyHostAddress(pThis->pSwitchPort,
1600 /* :fAdded */ false, kIntNetAddrType_IPv6, pAddr);
1601 break;
1602
1603 default:
1604 Log(("KEV INET6 event %u %.*s%d: addr %RTnaipv6\n",
1605 msg->event_code, IFNAMSIZ, link->if_name, link->if_unit, pAddr));
1606 break;
1607 }
1608 }
1609 else
1610 Log(("vboxNetFltDarwinSysSockUpcall: subclass %u ignored\n", (unsigned)msg->kev_subclass));
1611
1612 mbuf_freem(m);
1613 }
1614}
1615
1616
1617int vboxNetFltOsPreInitInstance(PVBOXNETFLTINS pThis)
1618{
1619 /*
1620 * Init the darwin specific members.
1621 */
1622 pThis->u.s.pIfNet = NULL;
1623 pThis->u.s.pIfFilter = NULL;
1624 pThis->u.s.fSetPromiscuous = false;
1625 pThis->u.s.fNeedSetPromiscuous = false;
1626 //pThis->u.s.MacAddr = {0};
1627 pThis->u.s.pSysSock = NULL;
1628
1629 return VINF_SUCCESS;
1630}
1631
1632
1633void vboxNetFltPortOsNotifyMacAddress(PVBOXNETFLTINS pThis, void *pvIfData, PCRTMAC pMac)
1634{
1635 NOREF(pThis); NOREF(pvIfData); NOREF(pMac);
1636}
1637
1638
1639int vboxNetFltPortOsConnectInterface(PVBOXNETFLTINS pThis, void *pvIf, void **ppvIfData)
1640{
1641 /* Nothing to do */
1642 NOREF(pThis); NOREF(pvIf); NOREF(ppvIfData);
1643 return VINF_SUCCESS;
1644}
1645
1646
1647int vboxNetFltPortOsDisconnectInterface(PVBOXNETFLTINS pThis, void *pvIfData)
1648{
1649 /* Nothing to do */
1650 NOREF(pThis); NOREF(pvIfData);
1651 return VINF_SUCCESS;
1652}
1653
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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