VirtualBox

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

Last change on this file since 57252 was 57252, checked in by vboxsync, 9 years ago

SUPDrv,VBoxNetFlt,VBoxNetAdp: Include the-darwin-kernel.h to avoid duplicating workarounds and EFLAGS.AC macros.

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