1 | /* $Id: VBoxNetAdapter-darwin.cpp 16741 2009-02-13 15:08:22Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * VBoxNetAdapter - Virtual Network Adapter Driver (Host), Darwin Specific Code.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2008 Sun Microsystems, Inc.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License (GPL) as published by the Free Software
|
---|
13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | *
|
---|
17 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
|
---|
18 | * Clara, CA 95054 USA or visit http://www.sun.com if you need
|
---|
19 | * additional information or have any questions.
|
---|
20 | */
|
---|
21 |
|
---|
22 | /*******************************************************************************
|
---|
23 | * Header Files *
|
---|
24 | *******************************************************************************/
|
---|
25 |
|
---|
26 | #define LOG_GROUP LOG_GROUP_NET_TAP_DRV
|
---|
27 | #include <VBox/log.h>
|
---|
28 | #include <VBox/err.h>
|
---|
29 | #include <iprt/assert.h>
|
---|
30 | #include <iprt/semaphore.h>
|
---|
31 | #include <iprt/uuid.h>
|
---|
32 |
|
---|
33 | #include <sys/systm.h>
|
---|
34 | __BEGIN_DECLS /* Buggy 10.4 headers, fixed in 10.5. */
|
---|
35 | #include <sys/kpi_mbuf.h>
|
---|
36 | __END_DECLS
|
---|
37 |
|
---|
38 | #include <net/ethernet.h>
|
---|
39 | #include <net/if_ether.h>
|
---|
40 | #include <net/if_types.h>
|
---|
41 | #include <sys/socket.h>
|
---|
42 | #include <net/if.h>
|
---|
43 | #include <net/if_dl.h>
|
---|
44 | #include <sys/errno.h>
|
---|
45 | #include <sys/param.h>
|
---|
46 |
|
---|
47 | #define VBOXNETADA_MAX_INSTANCES 8
|
---|
48 | #define VBOXNETADA_MAX_FAMILIES 4
|
---|
49 | #define VBOXNETADA_NAME "vboxnet"
|
---|
50 | #define VBOXNETADA_MTU 1500
|
---|
51 | #define VBOXNETADA_DETACH_TIMEOUT 500
|
---|
52 |
|
---|
53 | #define VBOXNETADA_FROM_IFACE(iface) ((PVBOXNETADA) ifnet_softc(iface))
|
---|
54 |
|
---|
55 | /**
|
---|
56 | * Void NETADAs mark vacant slots in NETADA array. Valid NETADAs are busy slots.
|
---|
57 | * As soon as slot is being modified its state changes to transitional.
|
---|
58 | * NETADAs in transitional state must only be accessed by the thread that
|
---|
59 | * put it to this state.
|
---|
60 | */
|
---|
61 | enum VBoxNetAdaState
|
---|
62 | {
|
---|
63 | VBOXNETADA_ST_VOID,
|
---|
64 | VBOXNETADA_ST_TRANSITIONAL,
|
---|
65 | VBOXNETADA_ST_VALID
|
---|
66 | };
|
---|
67 | typedef enum VBoxNetAdaState VBOXNETADASTATE;
|
---|
68 |
|
---|
69 | struct VBoxNetAda
|
---|
70 | {
|
---|
71 | /** Mutex protecting access to enmState. */
|
---|
72 | RTSEMFASTMUTEX hStateMtx;
|
---|
73 | /** Denotes availability of this NETADA. */
|
---|
74 | VBOXNETADASTATE enmState;
|
---|
75 | /** Corresponds to the digit at the end of NETADA name. */
|
---|
76 | uint8_t uUnit;
|
---|
77 | /** Event to signal detachment of interface. */
|
---|
78 | RTSEMEVENT hEvtDetached;
|
---|
79 | /** Pointer to Darwin interface structure. */
|
---|
80 | ifnet_t pIface;
|
---|
81 | /* todo: MAC address? */
|
---|
82 | /** Protocol families attached to this NETADA. */
|
---|
83 | protocol_family_t aAttachedFamilies[VBOXNETADA_MAX_FAMILIES];
|
---|
84 | };
|
---|
85 | typedef struct VBoxNetAda VBOXNETADA;
|
---|
86 | typedef VBOXNETADA *PVBOXNETADA;
|
---|
87 |
|
---|
88 | VBOXNETADA g_aAdapters[VBOXNETADA_MAX_INSTANCES];
|
---|
89 |
|
---|
90 | DECLINLINE(bool) vboxNetAdaIsValid(PVBOXNETADA pAda)
|
---|
91 | {
|
---|
92 | return pAda->enmState == VBOXNETADA_ST_VALID;
|
---|
93 | }
|
---|
94 |
|
---|
95 | DECLINLINE(bool) vboxNetAdaIsVoid(PVBOXNETADA pAda)
|
---|
96 | {
|
---|
97 | return pAda->enmState == VBOXNETADA_ST_VOID;
|
---|
98 | }
|
---|
99 |
|
---|
100 | static void vboxNetAdaComposeMACAddress(PVBOXNETADA pAda, PCRTMAC pCustomMac, struct sockaddr_dl *pMac)
|
---|
101 | {
|
---|
102 | pMac->sdl_len = sizeof(*pMac);
|
---|
103 | pMac->sdl_family = AF_LINK;
|
---|
104 | pMac->sdl_alen = ETHER_ADDR_LEN;
|
---|
105 | pMac->sdl_nlen = 0;
|
---|
106 | pMac->sdl_slen = 0;
|
---|
107 | if (pMac)
|
---|
108 | memcpy(LLADDR(pMac), pCustomMac->au8, ETHER_ADDR_LEN);
|
---|
109 | else
|
---|
110 | {
|
---|
111 | memcpy(LLADDR(pMac), "\0vbox0", pMac->sdl_alen);
|
---|
112 | LLADDR(pMac)[ETHER_ADDR_LEN - 1] += pAda->uUnit;
|
---|
113 | }
|
---|
114 | }
|
---|
115 |
|
---|
116 | static void vboxNetAdaComposeUUID(PVBOXNETADA pAda, PRTUUID pUuid)
|
---|
117 | {
|
---|
118 | /* Generate UUID from name and MAC address. */
|
---|
119 | RTUuidClear(pUuid);
|
---|
120 | memcpy(pUuid->au8, "vboxnet", 7);
|
---|
121 | pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
|
---|
122 | pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
|
---|
123 | pUuid->Gen.u8ClockSeqLow = pAda->uUnit;
|
---|
124 | memcpy(pUuid->Gen.au8Node, "\0vbox0", sizeof(pUuid->Gen.au8Node));
|
---|
125 | pUuid->Gen.au8Node[sizeof(pUuid->Gen.au8Node) - 1] += pAda->uUnit;
|
---|
126 | }
|
---|
127 |
|
---|
128 |
|
---|
129 | static errno_t vboxNetAdaOutput(ifnet_t pIface, mbuf_t pMBuf)
|
---|
130 | {
|
---|
131 | mbuf_freem_list(pMBuf);
|
---|
132 | return 0;
|
---|
133 | }
|
---|
134 |
|
---|
135 | static void vboxNetAdaAttachFamily(PVBOXNETADA pAda, protocol_family_t Family)
|
---|
136 | {
|
---|
137 | u_int32_t i;
|
---|
138 | for (i = 0; i < VBOXNETADA_MAX_FAMILIES; i++)
|
---|
139 | if (pAda->aAttachedFamilies[i] == 0)
|
---|
140 | {
|
---|
141 | pAda->aAttachedFamilies[i] = Family;
|
---|
142 | break;
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | static void vboxNetAdaDetachFamily(PVBOXNETADA pAda, protocol_family_t Family)
|
---|
147 | {
|
---|
148 | u_int32_t i;
|
---|
149 | for (i = 0; i < VBOXNETADA_MAX_FAMILIES; i++)
|
---|
150 | if (pAda->aAttachedFamilies[i] == Family)
|
---|
151 | pAda->aAttachedFamilies[i] = 0;
|
---|
152 | }
|
---|
153 |
|
---|
154 | static errno_t vboxNetAdaAddProto(ifnet_t pIface, protocol_family_t Family, const struct ifnet_demux_desc *pDemuxDesc, u_int32_t nDesc)
|
---|
155 | {
|
---|
156 | PVBOXNETADA pAda = VBOXNETADA_FROM_IFACE(pIface);
|
---|
157 | Assert(pAda);
|
---|
158 | vboxNetAdaAttachFamily(pAda, Family);
|
---|
159 | LogFlow(("vboxNetAdaAddProto: Family=%d.\n", Family));
|
---|
160 | return ether_add_proto(pIface, Family, pDemuxDesc, nDesc);
|
---|
161 | }
|
---|
162 |
|
---|
163 | static errno_t vboxNetAdaDelProto(ifnet_t pIface, protocol_family_t Family)
|
---|
164 | {
|
---|
165 | PVBOXNETADA pAda = VBOXNETADA_FROM_IFACE(pIface);
|
---|
166 | Assert(pAda);
|
---|
167 | LogFlow(("vboxNetAdaDelProto: Family=%d.\n", Family));
|
---|
168 | vboxNetAdaDetachFamily(pAda, Family);
|
---|
169 | return ether_del_proto(pIface, Family);
|
---|
170 | }
|
---|
171 |
|
---|
172 | static void vboxNetAdaDetach(ifnet_t pIface)
|
---|
173 | {
|
---|
174 | PVBOXNETADA pAda = VBOXNETADA_FROM_IFACE(pIface);
|
---|
175 | Assert(pAda);
|
---|
176 | Log2(("vboxNetAdaDetach: Signaling detach to vboxNetAdaUnregisterDevice.\n"));
|
---|
177 | /* Let vboxNetAdaUnregisterDevice know that the interface has been detached. */
|
---|
178 | RTSemEventSignal(pAda->hEvtDetached);
|
---|
179 | }
|
---|
180 |
|
---|
181 |
|
---|
182 | static int vboxNetAdaRegisterDevice(PVBOXNETADA pAda, const struct sockaddr_dl *pMACAddress)
|
---|
183 | {
|
---|
184 | struct ifnet_init_params Params;
|
---|
185 | RTUUID uuid;
|
---|
186 |
|
---|
187 | vboxNetAdaComposeUUID(pAda, &uuid);
|
---|
188 | Params.uniqueid = uuid.au8;
|
---|
189 | Params.uniqueid_len = sizeof(uuid);
|
---|
190 | Params.name = VBOXNETADA_NAME;
|
---|
191 | Params.unit = pAda->uUnit;
|
---|
192 | Params.family = IFNET_FAMILY_ETHERNET;
|
---|
193 | Params.type = IFT_ETHER;
|
---|
194 | Params.output = vboxNetAdaOutput;
|
---|
195 | Params.demux = ether_demux;
|
---|
196 | Params.add_proto = vboxNetAdaAddProto;
|
---|
197 | Params.del_proto = vboxNetAdaDelProto;
|
---|
198 | Params.check_multi = ether_check_multi;
|
---|
199 | Params.framer = ether_frameout;
|
---|
200 | Params.softc = pAda;
|
---|
201 | Params.ioctl = ether_ioctl;
|
---|
202 | Params.set_bpf_tap = NULL;
|
---|
203 | Params.detach = vboxNetAdaDetach;
|
---|
204 | Params.event = NULL;
|
---|
205 | Params.broadcast_addr = "\xFF\xFF\xFF\xFF\xFF\xFF";
|
---|
206 | Params.broadcast_len = ETHER_ADDR_LEN;
|
---|
207 |
|
---|
208 | errno_t err = ifnet_allocate(&Params, &pAda->pIface);
|
---|
209 | if (!err)
|
---|
210 | {
|
---|
211 | err = ifnet_attach(pAda->pIface, pMACAddress);
|
---|
212 | if (!err)
|
---|
213 | {
|
---|
214 | err = ifnet_set_flags(pAda->pIface, IFF_RUNNING | IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, 0xFFFF);
|
---|
215 | if (!err)
|
---|
216 | {
|
---|
217 | ifnet_set_mtu(pAda->pIface, VBOXNETADA_MTU);
|
---|
218 | return VINF_SUCCESS;
|
---|
219 | }
|
---|
220 | else
|
---|
221 | Log(("vboxNetAdaRegisterDevice: Failed to set flags (err=%d).\n", err));
|
---|
222 | ifnet_detach(pAda->pIface);
|
---|
223 | }
|
---|
224 | else
|
---|
225 | Log(("vboxNetAdaRegisterDevice: Failed to attach to interface (err=%d).\n", err));
|
---|
226 | ifnet_release(pAda->pIface);
|
---|
227 | }
|
---|
228 | else
|
---|
229 | Log(("vboxNetAdaRegisterDevice: Failed to allocate interface (err=%d).\n", err));
|
---|
230 |
|
---|
231 | return RTErrConvertFromErrno(err);
|
---|
232 | }
|
---|
233 |
|
---|
234 | static int vboxNetAdaUnregisterDevice(PVBOXNETADA pAda)
|
---|
235 | {
|
---|
236 | u_int32_t i;
|
---|
237 | /* Bring down the interface */
|
---|
238 | int rc = VINF_SUCCESS;
|
---|
239 | errno_t err = ifnet_set_flags(pAda->pIface, 0, IFF_UP | IFF_RUNNING);
|
---|
240 | if (err)
|
---|
241 | Log(("vboxNetAdaUnregisterDevice: Failed to bring down interface "
|
---|
242 | "(err=%d).\n", err));
|
---|
243 | /* Detach all protocols. */
|
---|
244 | for (i = 0; i < VBOXNETADA_MAX_FAMILIES; i++)
|
---|
245 | if (pAda->aAttachedFamilies[i])
|
---|
246 | ifnet_detach_protocol(pAda->pIface, pAda->aAttachedFamilies[i]);
|
---|
247 | err = ifnet_detach(pAda->pIface);
|
---|
248 | if (err)
|
---|
249 | Log(("vboxNetAdaUnregisterDevice: Failed to detach interface "
|
---|
250 | "(err=%d).\n", err));
|
---|
251 | Log2(("vboxNetAdaUnregisterDevice: Waiting for 'detached' event...\n"));
|
---|
252 | /* Wait until we get a signal from detach callback. */
|
---|
253 | rc = RTSemEventWait(pAda->hEvtDetached, VBOXNETADA_DETACH_TIMEOUT);
|
---|
254 | if (rc == VERR_TIMEOUT)
|
---|
255 | LogRel(("VBoxNETADA: Failed to detach interface %s%d\n.",
|
---|
256 | VBOXNETADA_NAME, pAda->uUnit));
|
---|
257 | err = ifnet_release(pAda->pIface);
|
---|
258 | if (err)
|
---|
259 | Log(("vboxNetAdaUnregisterDevice: Failed to release interface (err=%d).\n", err));
|
---|
260 |
|
---|
261 | return rc;
|
---|
262 | }
|
---|
263 |
|
---|
264 | int vboxNetAdaCreate (PVBOXNETADA *ppAda, PCRTMAC pMac)
|
---|
265 | {
|
---|
266 | int rc;
|
---|
267 | unsigned i;
|
---|
268 |
|
---|
269 | for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
|
---|
270 | {
|
---|
271 | PVBOXNETADA pAda = &g_aAdapters[i];
|
---|
272 | RTSemFastMutexRequest(pAda->hStateMtx);
|
---|
273 | if (vboxNetAdaIsVoid(pAda))
|
---|
274 | {
|
---|
275 | pAda->enmState = VBOXNETADA_ST_TRANSITIONAL;
|
---|
276 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
277 | /* Found an empty slot -- use it. */
|
---|
278 | struct sockaddr_dl mac;
|
---|
279 | Assert(pAda->hEvtDetached == NIL_RTSEMEVENT);
|
---|
280 | rc = RTSemEventCreate(&pAda->hEvtDetached);
|
---|
281 | if (RT_FAILURE(rc))
|
---|
282 | return rc;
|
---|
283 | pAda->uUnit = i;
|
---|
284 | vboxNetAdaComposeMACAddress(pAda, pMac, &mac);
|
---|
285 | rc = vboxNetAdaRegisterDevice(pAda, &mac);
|
---|
286 | *ppAda = pAda;
|
---|
287 | RTSemFastMutexRequest(pAda->hStateMtx);
|
---|
288 | pAda->enmState = VBOXNETADA_ST_VALID;
|
---|
289 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
290 | return rc;
|
---|
291 | }
|
---|
292 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
293 | }
|
---|
294 |
|
---|
295 | /* All slots in adapter array are busy. */
|
---|
296 | return VERR_OUT_OF_RESOURCES;
|
---|
297 | }
|
---|
298 |
|
---|
299 | int vboxNetAdaDestroy (PVBOXNETADA pAda)
|
---|
300 | {
|
---|
301 | int rc;
|
---|
302 |
|
---|
303 | RTSemFastMutexRequest(pAda->hStateMtx);
|
---|
304 | if (!vboxNetAdaIsValid(pAda))
|
---|
305 | {
|
---|
306 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
307 | return VERR_INVALID_PARAMETER;
|
---|
308 | }
|
---|
309 | pAda->enmState = VBOXNETADA_ST_TRANSITIONAL;
|
---|
310 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
311 |
|
---|
312 | rc = vboxNetAdaUnregisterDevice(pAda);
|
---|
313 | if (RT_FAILURE(rc))
|
---|
314 | Log(("vboxNetAdaDestroy: Failed to unregister device (rc=%Rrc).\n", rc));
|
---|
315 |
|
---|
316 | RTSemEventDestroy(pAda->hEvtDetached);
|
---|
317 | pAda->hEvtDetached = NIL_RTSEMEVENT;
|
---|
318 |
|
---|
319 | RTSemFastMutexRequest(pAda->hStateMtx);
|
---|
320 | pAda->enmState = VBOXNETADA_ST_VOID;
|
---|
321 | RTSemFastMutexRelease(pAda->hStateMtx);
|
---|
322 |
|
---|
323 | return rc;
|
---|
324 | }
|
---|
325 |
|
---|
326 | int vboxNetAdaModuleStart(void)
|
---|
327 | {
|
---|
328 | memset(&g_aAdapters, 0, sizeof(g_aAdapters));
|
---|
329 | for (unsigned i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
|
---|
330 | {
|
---|
331 | int rc = RTSemFastMutexCreate(&g_aAdapters[i].hStateMtx);
|
---|
332 | if (RT_FAILURE(rc))
|
---|
333 | {
|
---|
334 | Log(("vboxNetAdaModuleStart: Failed to create fast mutex (rc=%Rrc).\n", rc));
|
---|
335 | return rc;
|
---|
336 | }
|
---|
337 | }
|
---|
338 |
|
---|
339 | return VINF_SUCCESS;
|
---|
340 | }
|
---|
341 |
|
---|
342 | int vboxNetAdaModuleStop(void)
|
---|
343 | {
|
---|
344 | int rc;
|
---|
345 | unsigned i;
|
---|
346 |
|
---|
347 | for (i = 0; i < RT_ELEMENTS(g_aAdapters); i++)
|
---|
348 | {
|
---|
349 | vboxNetAdaDestroy(&g_aAdapters[i]);
|
---|
350 | rc = RTSemFastMutexDestroy(g_aAdapters[i].hStateMtx);
|
---|
351 | if (RT_FAILURE(rc))
|
---|
352 | Log(("vboxNetAdaModuleStop: Failed to destroy fast mutex (rc=%Rrc).\n", rc));
|
---|
353 | }
|
---|
354 |
|
---|
355 | return VINF_SUCCESS;
|
---|
356 | }
|
---|
357 |
|
---|