VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxNetAdp/darwin/VBoxNetAdp-darwin.cpp

Last change on this file was 106061, checked in by vboxsync, 6 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.1 KB
Line 
1/* $Id: VBoxNetAdp-darwin.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBoxNetAdp - Virtual Network Adapter Driver (Host), Darwin Specific Code.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP LOG_GROUP_NET_ADP_DRV
42#include "../../../Runtime/r0drv/darwin/the-darwin-kernel.h"
43
44#include <VBox/log.h>
45#include <VBox/err.h>
46#include <VBox/version.h>
47#include <iprt/assert.h>
48#include <iprt/initterm.h>
49#include <iprt/semaphore.h>
50#include <iprt/spinlock.h>
51#include <iprt/string.h>
52#include <iprt/uuid.h>
53#include <iprt/alloca.h>
54
55#include "../../darwin/VBoxNetSend.h"
56
57#include <sys/systm.h>
58RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
59#include <sys/kpi_mbuf.h>
60RT_C_DECLS_END
61
62#include <net/ethernet.h>
63#include <net/if_ether.h>
64#include <net/if_types.h>
65#include <sys/socket.h>
66#include <net/if.h>
67#include <net/if_dl.h>
68#include <sys/errno.h>
69#include <sys/param.h>
70#include <sys/conf.h>
71#include <miscfs/devfs/devfs.h>
72RT_C_DECLS_BEGIN
73#include <net/bpf.h>
74RT_C_DECLS_END
75
76#define VBOXNETADP_OS_SPECFIC 1
77#include "../VBoxNetAdpInternal.h"
78
79
80/*********************************************************************************************************************************
81* Defined Constants And Macros *
82*********************************************************************************************************************************/
83/** The maximum number of SG segments.
84 * Used to prevent stack overflow and similar bad stuff. */
85#define VBOXNETADP_DARWIN_MAX_SEGS 32
86#define VBOXNETADP_DARWIN_MAX_FAMILIES 4
87#define VBOXNETADP_DARWIN_NAME "vboxnet"
88#define VBOXNETADP_DARWIN_MTU 1500
89#define VBOXNETADP_DARWIN_DETACH_TIMEOUT 500
90
91#define VBOXNETADP_FROM_IFACE(iface) ((PVBOXNETADP) ifnet_softc(iface))
92
93
94/*********************************************************************************************************************************
95* Internal Functions *
96*********************************************************************************************************************************/
97RT_C_DECLS_BEGIN
98static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData);
99static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData);
100RT_C_DECLS_END
101
102static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
103static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess);
104static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess);
105
106
107/*********************************************************************************************************************************
108* Global Variables *
109*********************************************************************************************************************************/
110/**
111 * Declare the module stuff.
112 */
113RT_C_DECLS_BEGIN
114extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
115extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
116
117KMOD_EXPLICIT_DECL(VBoxNetAdp, VBOX_VERSION_STRING, _start, _stop)
118DECL_HIDDEN_DATA(kmod_start_func_t *) _realmain = VBoxNetAdpDarwinStart;
119DECL_HIDDEN_DATA(kmod_stop_func_t *) _antimain = VBoxNetAdpDarwinStop;
120DECL_HIDDEN_DATA(int) _kext_apple_cc = __APPLE_CC__;
121RT_C_DECLS_END
122
123/**
124 * The (common) global data.
125 */
126static int g_nCtlDev = -1; /* Major dev number */
127static void *g_hCtlDev = 0; /* FS dev handle */
128
129/**
130 * The character device switch table for the driver.
131 */
132static struct cdevsw g_ChDev =
133{
134 /*.d_open = */VBoxNetAdpDarwinOpen,
135 /*.d_close = */VBoxNetAdpDarwinClose,
136 /*.d_read = */eno_rdwrt,
137 /*.d_write = */eno_rdwrt,
138 /*.d_ioctl = */VBoxNetAdpDarwinIOCtl,
139 /*.d_stop = */eno_stop,
140 /*.d_reset = */eno_reset,
141 /*.d_ttys = */NULL,
142 /*.d_select = */eno_select,
143 /*.d_mmap = */eno_mmap,
144 /*.d_strategy = */eno_strat,
145 /*.d_getc = */(void *)(uintptr_t)&enodev, //eno_getc,
146 /*.d_putc = */(void *)(uintptr_t)&enodev, //eno_putc,
147 /*.d_type = */0
148};
149
150
151
152static void vboxNetAdpDarwinComposeUUID(PVBOXNETADP pThis, PRTUUID pUuid)
153{
154 /* Generate UUID from name and MAC address. */
155 RTUuidClear(pUuid);
156 memcpy(pUuid->au8, "vboxnet", 7);
157 pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
158 pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
159 pUuid->Gen.u8ClockSeqLow = pThis->iUnit;
160 vboxNetAdpComposeMACAddress(pThis, (PRTMAC)pUuid->Gen.au8Node);
161}
162
163static errno_t vboxNetAdpDarwinOutput(ifnet_t pIface, mbuf_t pMBuf)
164{
165 /*
166 * We are a dummy interface with all the real work done in
167 * VBoxNetFlt bridged networking filter. If anything makes it
168 * this far, it must be a broadcast or a packet for an unknown
169 * guest that intnet didn't know where to dispatch. In that case
170 * we must still do the BPF tap and stats.
171 */
172 bpf_tap_out(pIface, DLT_EN10MB, pMBuf, NULL, 0);
173 ifnet_stat_increment_out(pIface, 1, mbuf_len(pMBuf), 0);
174
175 mbuf_freem_list(pMBuf);
176 return 0;
177}
178
179static void vboxNetAdpDarwinDetach(ifnet_t pIface)
180{
181 PVBOXNETADP pThis = VBOXNETADP_FROM_IFACE(pIface);
182 Assert(pThis);
183 Log2(("vboxNetAdpDarwinDetach: Signaling detach to vboxNetAdpUnregisterDevice.\n"));
184 /* Let vboxNetAdpDarwinUnregisterDevice know that the interface has been detached. */
185 RTSemEventSignal(pThis->u.s.hEvtDetached);
186}
187
188static errno_t vboxNetAdpDarwinDemux(ifnet_t pIface, mbuf_t pMBuf,
189 char *pFrameHeader,
190 protocol_family_t *pProtocolFamily)
191{
192 /*
193 * Anything we get here comes from VBoxNetFlt bridged networking
194 * filter where it has already been accounted for and fed to bpf.
195 */
196 return ether_demux(pIface, pMBuf, pFrameHeader, pProtocolFamily);
197}
198
199
200static errno_t vboxNetAdpDarwinIfIOCtl(ifnet_t pIface, unsigned long uCmd, void *pvData)
201{
202 errno_t error = 0;
203
204 if (pvData == NULL)
205 {
206 /*
207 * Common pattern in the kernel code is to make changes in the
208 * net layer and then notify the device driver by calling its
209 * ioctl function with NULL parameter, e.g.:
210 *
211 * ifnet_set_flags(interface, ...);
212 * ifnet_ioctl(interface, 0, SIOCSIFFLAGS, NULL);
213 *
214 * These are no-ops for us, so tell the caller we succeeded
215 * because some callers do check that return value.
216 */
217 switch (uCmd)
218 {
219 case SIOCSIFFLAGS:
220 Log2(("VBoxNetAdp: %s%d: SIOCSIFFLAGS (null): flags = 0x%04hx\n",
221 ifnet_name(pIface), ifnet_unit(pIface),
222 (uint16_t)ifnet_flags(pIface)));
223 return 0;
224
225 case SIOCADDMULTI:
226 case SIOCDELMULTI:
227 Log2(("VBoxNetAdp: %s%d: SIOC%sMULTI (null)\n",
228 ifnet_name(pIface), ifnet_unit(pIface),
229 uCmd == SIOCADDMULTI ? "ADD" : "DEL"));
230 return 0;
231 }
232 }
233
234 Log2(("VBoxNetAdp: %s%d: %c%c '%c' %u len %u\n",
235 ifnet_name(pIface), ifnet_unit(pIface),
236 uCmd & IOC_OUT ? '<' : '-',
237 uCmd & IOC_IN ? '>' : '-',
238 IOCGROUP(uCmd),
239 uCmd & 0xff,
240 IOCPARM_LEN(uCmd)));
241
242 error = ether_ioctl(pIface, uCmd, pvData);
243 return error;
244}
245
246
247int vboxNetAdpOsCreate(PVBOXNETADP pThis, PCRTMAC pMACAddress)
248{
249 int rc;
250 struct ifnet_init_params Params;
251 RTUUID uuid;
252 struct sockaddr_dl mac;
253
254 pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
255 rc = RTSemEventCreate(&pThis->u.s.hEvtDetached);
256 if (RT_FAILURE(rc))
257 {
258 printf("vboxNetAdpOsCreate: failed to create semaphore (rc=%d).\n", rc);
259 return rc;
260 }
261
262 mac.sdl_len = sizeof(mac);
263 mac.sdl_family = AF_LINK;
264 mac.sdl_alen = ETHER_ADDR_LEN;
265 mac.sdl_nlen = 0;
266 mac.sdl_slen = 0;
267 memcpy(LLADDR(&mac), pMACAddress->au8, mac.sdl_alen);
268
269 RTStrPrintf(pThis->szName, VBOXNETADP_MAX_NAME_LEN, "%s%d", VBOXNETADP_NAME, pThis->iUnit);
270 vboxNetAdpDarwinComposeUUID(pThis, &uuid);
271 Params.uniqueid = uuid.au8;
272 Params.uniqueid_len = sizeof(uuid);
273 Params.name = VBOXNETADP_NAME;
274 Params.unit = pThis->iUnit;
275 Params.family = IFNET_FAMILY_ETHERNET;
276 Params.type = IFT_ETHER;
277 Params.output = vboxNetAdpDarwinOutput;
278 Params.demux = vboxNetAdpDarwinDemux;
279 Params.add_proto = ether_add_proto;
280 Params.del_proto = ether_del_proto;
281 Params.check_multi = ether_check_multi;
282 Params.framer = ether_frameout;
283 Params.softc = pThis;
284 Params.ioctl = vboxNetAdpDarwinIfIOCtl;
285 Params.set_bpf_tap = NULL;
286 Params.detach = vboxNetAdpDarwinDetach;
287 Params.event = NULL;
288 Params.broadcast_addr = "\xFF\xFF\xFF\xFF\xFF\xFF";
289 Params.broadcast_len = ETHER_ADDR_LEN;
290
291 errno_t err = ifnet_allocate(&Params, &pThis->u.s.pIface);
292 if (!err)
293 {
294 err = ifnet_attach(pThis->u.s.pIface, &mac);
295 if (!err)
296 {
297 bpfattach(pThis->u.s.pIface, DLT_EN10MB, ETHER_HDR_LEN);
298
299 err = ifnet_set_flags(pThis->u.s.pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
300 if (!err)
301 {
302 ifnet_set_mtu(pThis->u.s.pIface, VBOXNETADP_MTU);
303 VBoxNetSendDummy(pThis->u.s.pIface);
304 return VINF_SUCCESS;
305 }
306 else
307 Log(("vboxNetAdpDarwinRegisterDevice: Failed to set flags (err=%d).\n", err));
308 ifnet_detach(pThis->u.s.pIface);
309 }
310 else
311 Log(("vboxNetAdpDarwinRegisterDevice: Failed to attach to interface (err=%d).\n", err));
312 ifnet_release(pThis->u.s.pIface);
313 }
314 else
315 Log(("vboxNetAdpDarwinRegisterDevice: Failed to allocate interface (err=%d).\n", err));
316
317 RTSemEventDestroy(pThis->u.s.hEvtDetached);
318 pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
319
320 return RTErrConvertFromErrno(err);
321}
322
323void vboxNetAdpOsDestroy(PVBOXNETADP pThis)
324{
325 /* Bring down the interface */
326 int rc = VINF_SUCCESS;
327 errno_t err;
328
329 AssertPtr(pThis->u.s.pIface);
330 Assert(pThis->u.s.hEvtDetached != NIL_RTSEMEVENT);
331
332 err = ifnet_set_flags(pThis->u.s.pIface, 0, IFF_UP | IFF_RUNNING);
333 if (err)
334 Log(("vboxNetAdpDarwinUnregisterDevice: Failed to bring down interface "
335 "(err=%d).\n", err));
336 err = ifnet_detach(pThis->u.s.pIface);
337 if (err)
338 Log(("vboxNetAdpDarwinUnregisterDevice: Failed to detach interface "
339 "(err=%d).\n", err));
340 Log2(("vboxNetAdpDarwinUnregisterDevice: Waiting for 'detached' event...\n"));
341 /* Wait until we get a signal from detach callback. */
342 rc = RTSemEventWait(pThis->u.s.hEvtDetached, VBOXNETADP_DETACH_TIMEOUT);
343 if (rc == VERR_TIMEOUT)
344 LogRel(("VBoxAdpDrv: Failed to detach interface %s%d\n.",
345 VBOXNETADP_NAME, pThis->iUnit));
346 err = ifnet_release(pThis->u.s.pIface);
347 if (err)
348 Log(("vboxNetAdpUnregisterDevice: Failed to release interface (err=%d).\n", err));
349
350 RTSemEventDestroy(pThis->u.s.hEvtDetached);
351 pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
352}
353
354/**
355 * Device open. Called on open /dev/vboxnetctl
356 *
357 * @param Dev The device number.
358 * @param fFlags ???.
359 * @param fDevType ???.
360 * @param pProcess The process issuing this request.
361 */
362static int VBoxNetAdpDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
363{
364 RT_NOREF(Dev, fFlags, fDevType, pProcess);
365#ifdef LOG_ENABLED
366 char szName[128];
367 szName[0] = '\0';
368 proc_name(proc_pid(pProcess), szName, sizeof(szName));
369 Log(("VBoxNetAdpDarwinOpen: pid=%d '%s'\n", proc_pid(pProcess), szName));
370#endif
371 return 0;
372}
373
374/**
375 * Close device.
376 */
377static int VBoxNetAdpDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess)
378{
379 RT_NOREF(Dev, fFlags, fDevType, pProcess);
380 Log(("VBoxNetAdpDarwinClose: pid=%d\n", proc_pid(pProcess)));
381 return 0;
382}
383
384/**
385 * Device I/O Control entry point.
386 *
387 * @returns Darwin for slow IOCtls and VBox status code for the fast ones.
388 * @param Dev The device number (major+minor).
389 * @param iCmd The IOCtl command.
390 * @param pData Pointer to the data (if any it's a SUPDRVIOCTLDATA (kernel copy)).
391 * @param fFlags Flag saying we're a character device (like we didn't know already).
392 * @param pProcess The process issuing this request.
393 */
394static int VBoxNetAdpDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess)
395{
396 RT_NOREF(Dev, fFlags, pProcess);
397 uint32_t cbReq = IOCPARM_LEN(iCmd);
398 PVBOXNETADPREQ pReq = (PVBOXNETADPREQ)pData;
399 int rc;
400
401 Log(("VBoxNetAdpDarwinIOCtl: param len %#x; iCmd=%#lx\n", cbReq, iCmd));
402 switch (IOCBASECMD(iCmd))
403 {
404 case IOCBASECMD(VBOXNETADP_CTL_ADD):
405 {
406 if ( (IOC_DIRMASK & iCmd) != IOC_INOUT
407 || cbReq < sizeof(VBOXNETADPREQ))
408 return EINVAL;
409
410 PVBOXNETADP pNew;
411 Log(("VBoxNetAdpDarwinIOCtl: szName=%s\n", pReq->szName));
412 rc = vboxNetAdpCreate(&pNew,
413 pReq->szName[0] && RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))) ?
414 pReq->szName : NULL);
415 if (RT_FAILURE(rc))
416 return rc == VERR_OUT_OF_RESOURCES ? ENOMEM : EINVAL;
417
418 Assert(strlen(pReq->szName) < sizeof(pReq->szName));
419 strncpy(pReq->szName, pNew->szName, sizeof(pReq->szName) - 1);
420 pReq->szName[sizeof(pReq->szName) - 1] = '\0';
421 Log(("VBoxNetAdpDarwinIOCtl: Added '%s'\n", pReq->szName));
422 break;
423 }
424
425 case IOCBASECMD(VBOXNETADP_CTL_REMOVE):
426 {
427 if (!RTStrEnd(pReq->szName, RT_MIN(cbReq, sizeof(pReq->szName))))
428 return EINVAL;
429
430 PVBOXNETADP pAdp = vboxNetAdpFindByName(pReq->szName);
431 if (!pAdp)
432 return EINVAL;
433
434 rc = vboxNetAdpDestroy(pAdp);
435 if (RT_FAILURE(rc))
436 return EINVAL;
437 Log(("VBoxNetAdpDarwinIOCtl: Removed %s\n", pReq->szName));
438 break;
439 }
440
441 default:
442 printf("VBoxNetAdpDarwinIOCtl: unknown command %lx.\n", IOCBASECMD(iCmd));
443 return EINVAL;
444 }
445
446 return 0;
447}
448
449int vboxNetAdpOsInit(PVBOXNETADP pThis)
450{
451 /*
452 * Init the darwin specific members.
453 */
454 pThis->u.s.pIface = NULL;
455 pThis->u.s.hEvtDetached = NIL_RTSEMEVENT;
456
457 return VINF_SUCCESS;
458}
459
460/**
461 * Start the kernel module.
462 */
463static kern_return_t VBoxNetAdpDarwinStart(struct kmod_info *pKModInfo, void *pvData)
464{
465 RT_NOREF(pKModInfo, pvData);
466 int rc;
467
468 /*
469 * Initialize IPRT and find our module tag id.
470 * (IPRT is shared with VBoxDrv, it creates the loggers.)
471 */
472 rc = RTR0Init(0);
473 if (RT_SUCCESS(rc))
474 {
475 Log(("VBoxNetAdpDarwinStart\n"));
476 rc = vboxNetAdpInit();
477 if (RT_SUCCESS(rc))
478 {
479 g_nCtlDev = cdevsw_add(-1, &g_ChDev);
480 if (g_nCtlDev < 0)
481 {
482 LogRel(("VBoxAdp: failed to register control device."));
483 rc = VERR_CANT_CREATE;
484 }
485 else
486 {
487 g_hCtlDev = devfs_make_node(makedev(g_nCtlDev, 0), DEVFS_CHAR,
488 UID_ROOT, GID_WHEEL, 0600, VBOXNETADP_CTL_DEV_NAME);
489 if (!g_hCtlDev)
490 {
491 LogRel(("VBoxAdp: failed to create FS node for control device."));
492 rc = VERR_CANT_CREATE;
493 }
494 }
495 }
496
497 if (RT_SUCCESS(rc))
498 {
499 LogRel(("VBoxAdpDrv: version " VBOX_VERSION_STRING " r%d\n", VBOX_SVN_REV));
500 return KMOD_RETURN_SUCCESS;
501 }
502
503 LogRel(("VBoxAdpDrv: failed to initialize device extension (rc=%d)\n", rc));
504 RTR0Term();
505 }
506 else
507 printf("VBoxAdpDrv: failed to initialize IPRT (rc=%d)\n", rc);
508
509 return KMOD_RETURN_FAILURE;
510}
511
512
513/**
514 * Stop the kernel module.
515 */
516static kern_return_t VBoxNetAdpDarwinStop(struct kmod_info *pKModInfo, void *pvData)
517{
518 RT_NOREF(pKModInfo, pvData);
519 Log(("VBoxNetAdpDarwinStop\n"));
520
521 vboxNetAdpShutdown();
522 /* Remove control device */
523 devfs_remove(g_hCtlDev);
524 cdevsw_remove(g_nCtlDev, &g_ChDev);
525
526 RTR0Term();
527
528 return KMOD_RETURN_SUCCESS;
529}
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