VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/DrvNATlibslirp.cpp@ 105069

Last change on this file since 105069 was 105069, checked in by vboxsync, 10 months ago

Devices/Network: clean up code and comments. bugref:10268

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.7 KB
Line 
1/* $Id: DrvNATlibslirp.cpp 105069 2024-06-27 20:26:21Z vboxsync $ */
2/** @file
3 * DrvNATlibslirp - NATlibslirp network transport driver.
4 */
5
6/*
7 * Copyright (C) 2022-2023 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 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_NAT
33#define __STDC_LIMIT_MACROS
34#define __STDC_CONSTANT_MACROS
35
36#include "DrvNATlibslirp.h"
37
38
39/*********************************************************************************************************************************
40* Internal Functions *
41*********************************************************************************************************************************/
42
43/**
44 * @callback_method_impl{FNPDMTHREADDRV}
45 *
46 * Queues guest process received packet. Triggered by drvNATRecvWakeup.
47 */
48static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
49{
50 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
51
52 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
53 return VINF_SUCCESS;
54
55 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
56 {
57 RTReqQueueProcess(pThis->hRecvReqQueue, 0);
58 if (ASMAtomicReadU32(&pThis->cPkts) == 0)
59 RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
60 }
61 return VINF_SUCCESS;
62}
63
64/**
65 * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
66 */
67static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
68{
69 RT_NOREF(pThread);
70 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
71 int rc;
72 rc = RTSemEventSignal(pThis->EventRecv);
73
74 STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
75 return VINF_SUCCESS;
76}
77
78/**
79 * @callback_method_impl{FNPDMTHREADDRV}
80 */
81static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
82{
83 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
84
85 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
86 return VINF_SUCCESS;
87
88 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
89 {
90 RTReqQueueProcess(pThis->hUrgRecvReqQueue, 0);
91 if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
92 {
93 int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
94 AssertRC(rc);
95 }
96 }
97 return VINF_SUCCESS;
98}
99
100/**
101 * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
102 */
103static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
104{
105 RT_NOREF(pThread);
106 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
107 int rc = RTSemEventSignal(pThis->EventUrgRecv);
108 AssertRC(rc);
109
110 return VINF_SUCCESS;
111}
112
113/**
114 * @brief Processes incoming packet (to guest).
115 *
116 * @param pThis Pointer to DRVNAT state for current context.
117 * @param pBuf Pointer to packet buffer.
118 * @param cb Size of packet in buffer.
119 *
120 * @thread NAT
121 */
122static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, void *pBuf, int cb)
123{
124 int rc;
125 STAM_PROFILE_START(&pThis->StatNATRecv, a);
126
127 while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
128 {
129 rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
130 if ( RT_FAILURE(rc)
131 && ( rc == VERR_TIMEOUT
132 || rc == VERR_INTERRUPTED))
133 goto done_unlocked;
134 }
135
136 rc = RTCritSectEnter(&pThis->DevAccessLock);
137 AssertRC(rc);
138
139 STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
140 rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
141 STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
142
143 if (RT_SUCCESS(rc))
144 {
145 rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pBuf, cb);
146 AssertRC(rc);
147 RTMemFree(pBuf);
148 pBuf = NULL;
149 }
150 else if ( rc != VERR_TIMEOUT
151 && rc != VERR_INTERRUPTED)
152 {
153 AssertRC(rc);
154 }
155
156 rc = RTCritSectLeave(&pThis->DevAccessLock);
157 AssertRC(rc);
158
159done_unlocked:
160 ASMAtomicDecU32(&pThis->cPkts);
161
162 drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
163
164 RTMemFree(pBuf);
165 pBuf = NULL;
166
167 STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
168}
169
170/**
171 * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
172 *
173 * @param pThis Pointer to the NAT instance.
174 * @param pSgBuf The S/G buffer to free.
175 *
176 * @thread NAT
177 */
178static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
179{
180 RT_NOREF(pThis);
181 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
182 pSgBuf->fFlags = 0;
183 if (pSgBuf->pvAllocator)
184 {
185 Assert(!pSgBuf->pvUser);
186 RTMemFree(pSgBuf->aSegs[0].pvSeg);
187 }
188 else if (pSgBuf->pvUser)
189 {
190 RTMemFree(pSgBuf->aSegs[0].pvSeg);
191 pSgBuf->aSegs[0].pvSeg = NULL;
192 RTMemFree(pSgBuf->pvUser);
193 pSgBuf->pvUser = NULL;
194 }
195 RTMemFree(pSgBuf);
196}
197
198/**
199 * Worker function for drvNATSend().
200 *
201 * @param pThis Pointer to the NAT instance.
202 * @param pSgBuf The scatter/gather buffer.
203 * @thread NAT
204 */
205static DECLCALLBACK(void) drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
206{
207 LogFlowFunc(("pThis=%p pSgBuf=%p\n", pThis, pSgBuf));
208
209 if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
210 {
211 const uint8_t *m = static_cast<const uint8_t*>(pSgBuf->pvAllocator);
212 if (m)
213 {
214 /*
215 * A normal frame.
216 */
217 LogFlowFunc(("m=%p\n", m));
218 slirp_input(pThis->pNATState->pSlirp, (uint8_t const *)pSgBuf->pvAllocator, (int)pSgBuf->cbUsed);
219 }
220 else
221 {
222 /*
223 * M_EXT buf, need to segment it.
224 */
225
226 uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
227 PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
228 /* Do not attempt to segment frames with invalid GSO parameters. */
229 if (PDMNetGsoIsValid((const PDMNETWORKGSO *)pGso, sizeof(*pGso), pSgBuf->cbUsed))
230 {
231 uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed);
232 Assert(cSegs > 1);
233 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
234 {
235 void *pvSeg;
236
237 /** @todo r=jack: is this fine leaving as a constant instead of dynamic? */
238 pvSeg = RTMemAlloc(DRVNAT_MAXFRAMESIZE);
239
240 uint32_t cbPayload, cbHdrs;
241 uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
242 iSeg, cSegs, (uint8_t *)pvSeg, &cbHdrs, &cbPayload);
243 memcpy((uint8_t *)pvSeg + cbHdrs, pbFrame + offPayload, cbPayload);
244
245 slirp_input(pThis->pNATState->pSlirp, (uint8_t const *)pvSeg, cbPayload + cbHdrs);
246 RTMemFree(pvSeg);
247 }
248 }
249 }
250 }
251
252 LogFlowFunc(("leave\n"));
253 drvNATFreeSgBuf(pThis, pSgBuf);
254}
255
256/**
257 * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
258 */
259static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
260{
261 RT_NOREF(fOnWorkerThread);
262 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
263 int rc = RTCritSectTryEnter(&pThis->XmitLock);
264 if (RT_FAILURE(rc))
265 {
266 /** @todo Kick the worker thread when we have one... */
267 rc = VERR_TRY_AGAIN;
268 }
269 LogFlowFunc(("Beginning xmit...\n"));
270 return rc;
271}
272
273/**
274 * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
275 */
276static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
277 PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
278{
279 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
280 Assert(RTCritSectIsOwner(&pThis->XmitLock));
281
282 LogFlowFunc(("enter\n"));
283
284 /*
285 * Drop the incoming frame if the NAT thread isn't running.
286 */
287 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
288 {
289 Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
290 return VERR_NET_NO_NETWORK;
291 }
292
293 /*
294 * Allocate a scatter/gather buffer and an mbuf.
295 */
296 PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAllocZ(sizeof(PDMSCATTERGATHER));
297 if (!pSgBuf)
298 return VERR_NO_MEMORY;
299 if (!pGso)
300 {
301 /*
302 * Drop the frame if it is too big.
303 */
304 if (cbMin >= DRVNAT_MAXFRAMESIZE)
305 {
306 Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
307 cbMin));
308 RTMemFree(pSgBuf);
309 return VERR_INVALID_PARAMETER;
310 }
311
312 pSgBuf->pvUser = NULL;
313 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 128);
314 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
315 pSgBuf->pvAllocator = pSgBuf->aSegs[0].pvSeg;
316
317 if (!pSgBuf->pvAllocator)
318 {
319 RTMemFree(pSgBuf);
320 return VERR_TRY_AGAIN;
321 }
322 }
323 else
324 {
325 /*
326 * Drop the frame if its segment is too big.
327 */
328 if (pGso->cbHdrsTotal + pGso->cbMaxSeg >= DRVNAT_MAXFRAMESIZE)
329 {
330 Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
331 pGso->cbHdrsTotal + pGso->cbMaxSeg));
332 RTMemFree(pSgBuf);
333 return VERR_INVALID_PARAMETER;
334 }
335
336 pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
337 pSgBuf->pvAllocator = NULL;
338
339 /** @todo r=jack: figure out why need *2 */
340 pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin*2, 128);
341 pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
342 if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
343 {
344 RTMemFree(pSgBuf->aSegs[0].pvSeg);
345 RTMemFree(pSgBuf->pvUser);
346 RTMemFree(pSgBuf);
347 return VERR_TRY_AGAIN;
348 }
349 }
350
351 /*
352 * Initialize the S/G buffer and return.
353 */
354 pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
355 pSgBuf->cbUsed = 0;
356 pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
357 pSgBuf->cSegs = 1;
358
359 *ppSgBuf = pSgBuf;
360 return VINF_SUCCESS;
361}
362
363/**
364 * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
365 */
366static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
367{
368 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
369 Assert(RTCritSectIsOwner(&pThis->XmitLock));
370 drvNATFreeSgBuf(pThis, pSgBuf);
371 return VINF_SUCCESS;
372}
373
374/**
375 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
376 */
377static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
378{
379 RT_NOREF(fOnWorkerThread);
380 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
381 Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
382 Assert(RTCritSectIsOwner(&pThis->XmitLock));
383
384 LogFlowFunc(("enter\n"));
385
386 int rc;
387 if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
388 {
389 rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
390 RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
391 (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
392 if (RT_SUCCESS(rc))
393 {
394 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
395 LogFlowFunc(("leave success\n"));
396 return VINF_SUCCESS;
397 }
398
399 rc = VERR_NET_NO_BUFFER_SPACE;
400 }
401 else
402 rc = VERR_NET_DOWN;
403 drvNATFreeSgBuf(pThis, pSgBuf);
404 LogFlowFunc(("leave rc=%Rrc\n", rc));
405 return rc;
406}
407
408/**
409 * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
410 */
411static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
412{
413 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
414 RTCritSectLeave(&pThis->XmitLock);
415}
416
417/**
418 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
419 */
420static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
421{
422 RT_NOREF(pszWho);
423 int rc;
424#ifndef RT_OS_WINDOWS
425 /* kick poll() */
426 size_t cbIgnored;
427 rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
428#else
429 /* kick WSAWaitForMultipleEvents */
430 rc = WSASetEvent(pThis->hWakeupEvent);
431#endif
432 AssertRC(rc);
433}
434
435/**
436 * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
437 */
438static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
439{
440 RT_NOREF(pInterface, fPromiscuous);
441 LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
442 /* nothing to do */
443}
444
445/**
446 * Worker function for drvNATNetworkUp_NotifyLinkChanged().
447 * @thread "NAT" thread.
448 *
449 * @param pThis Pointer to DRVNAT state for current context.
450 * @param enmLinkState Enum value of link state.
451 *
452 * @thread NAT
453 */
454static DECLCALLBACK(void) drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
455{
456 pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
457 switch (enmLinkState)
458 {
459 case PDMNETWORKLINKSTATE_UP:
460 LogRel(("NAT: Link up\n"));
461 break;
462
463 case PDMNETWORKLINKSTATE_DOWN:
464 case PDMNETWORKLINKSTATE_DOWN_RESUME:
465 LogRel(("NAT: Link down\n"));
466 break;
467
468 default:
469 AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
470 }
471}
472
473/**
474 * Notification on link status changes.
475 *
476 * @param pInterface Pointer to the interface structure containing the called function pointer.
477 * @param enmLinkState The new link state.
478 *
479 * @thread EMT
480 */
481static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
482{
483 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
484
485 LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
486
487 /* Don't queue new requests if the NAT thread is not running (e.g. paused,
488 * stopping), otherwise we would deadlock. Memorize the change. */
489 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
490 {
491 pThis->enmLinkStateWant = enmLinkState;
492 return;
493 }
494
495 PRTREQ pReq;
496 int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
497 (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
498 if (rc == VERR_TIMEOUT)
499 {
500 drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
501 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
502 AssertRC(rc);
503 }
504 else
505 AssertRC(rc);
506 RTReqRelease(pReq);
507}
508
509/**
510 * Registers poll. Unused function (other than logging).
511 */
512static void drvNAT_RegisterPoll(int fd, void *opaque) {
513 RT_NOREF(fd, opaque);
514 Log4(("Poll registered\n"));
515}
516
517/**
518 * Unregisters poll. Unused function (other than logging).
519 */
520static void drvNAT_UnregisterPoll(int fd, void *opaque) {
521 RT_NOREF(fd, opaque);
522 Log4(("Poll unregistered\n"));
523}
524
525/**
526 * Converts slirp representation of poll events to host representation.
527 *
528 * @param iEvents Integer representing slirp type poll events.
529 *
530 * @returns Integer representing host type poll events.
531 *
532 * @thread ?
533 */
534static int drvNAT_PollEventSlirpToHost(int iEvents) {
535 int iRet = 0;
536#ifndef RT_OS_WINDOWS
537 if (iEvents & SLIRP_POLL_IN) iRet |= POLLIN;
538 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLOUT;
539 if (iEvents & SLIRP_POLL_PRI) iRet |= POLLPRI;
540 if (iEvents & SLIRP_POLL_ERR) iRet |= POLLERR;
541 if (iEvents & SLIRP_POLL_HUP) iRet |= POLLHUP;
542#else
543 if (iEvents & SLIRP_POLL_IN) iRet |= (POLLRDNORM | POLLRDBAND);
544 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLWRNORM;
545 if (iEvents & SLIRP_POLL_PRI) iRet |= (POLLIN);
546 if (iEvents & SLIRP_POLL_ERR) iRet |= 0;
547 if (iEvents & SLIRP_POLL_HUP) iRet |= 0;
548#endif
549 return iRet;
550}
551
552/**
553 * Converts host representation of poll events to slirp representation.
554 *
555 * @param iEvents Integer representing host type poll events.
556 *
557 * @returns Integer representing slirp type poll events.
558 *
559 * @thread ?
560 */
561static int drvNAT_PollEventHostToSlirp(int iEvents) {
562 int iRet = 0;
563#ifndef RT_OS_WINDOWS
564 if (iEvents & POLLIN) iRet |= SLIRP_POLL_IN;
565 if (iEvents & POLLOUT) iRet |= SLIRP_POLL_OUT;
566 if (iEvents & POLLPRI) iRet |= SLIRP_POLL_PRI;
567 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
568 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
569#else
570 if (iEvents & (POLLRDNORM | POLLRDBAND)) iRet |= SLIRP_POLL_IN;
571 if (iEvents & POLLWRNORM) iRet |= SLIRP_POLL_OUT;
572 if (iEvents & (POLLPRI)) iRet |= SLIRP_POLL_PRI;
573 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
574 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
575#endif
576 return iRet;
577}
578
579/**
580 * Callback function to add entry to pollfd array.
581 *
582 * @param iFd Integer of system file descriptor of socket.
583 * (on windows, this is a VBox internal, not system, value).
584 * @param iEvents Integer of slirp type poll events.
585 * @param opaque Pointer to NAT State context.
586 *
587 * @returns Index of latest pollfd entry.
588 *
589 * @thread ?
590 */
591static int drvNAT_addPollCb(int iFd, int iEvents, void *opaque)
592{
593 PDRVNAT pThis = (PDRVNAT)opaque;
594
595 if (pThis->pNATState->nsock + 1 >= pThis->pNATState->uPollCap)
596 {
597 int cbNew = pThis->pNATState->uPollCap * 2 * sizeof(struct pollfd);
598 struct pollfd *pvNew = (struct pollfd *)RTMemRealloc(pThis->pNATState->polls, cbNew);
599 if(pvNew)
600 {
601 pThis->pNATState->polls = pvNew;
602 pThis->pNATState->uPollCap *= 2;
603 }
604 else
605 return -1;
606 }
607
608 int idx = pThis->pNATState->nsock;
609#ifdef RT_OS_WINDOWS
610 pThis->pNATState->polls[idx].fd = libslirp_wrap_RTHandleTableLookup(iFd);
611#else
612 pThis->pNATState->polls[idx].fd = iFd;
613#endif
614 pThis->pNATState->polls[idx].events = drvNAT_PollEventSlirpToHost(iEvents);
615 pThis->pNATState->polls[idx].revents = 0;
616 pThis->pNATState->nsock += 1;
617 return idx;
618}
619
620/**
621 * Get translated revents from a poll at a given index.
622 *
623 * @param idx Integer index of poll.
624 * @param opaque Pointer to NAT State context.
625 *
626 * @returns Integer representing transalted revents.
627 *
628 * @thread ?
629 */
630static int get_revents_cb(int idx, void *opaque)
631{
632 PDRVNAT pThis = (PDRVNAT)opaque;
633 struct pollfd* polls = pThis->pNATState->polls;
634 return drvNAT_PollEventHostToSlirp(polls[idx].revents);
635}
636
637/**
638 * NAT thread handling the slirp stuff.
639 *
640 * The slirp implementation is single-threaded so we execute this enginre in a
641 * dedicated thread. We take care that this thread does not become the
642 * bottleneck: If the guest wants to send, a request is enqueued into the
643 * hSlirpReqQueue and handled asynchronously by this thread. If this thread
644 * wants to deliver packets to the guest, it enqueues a request into
645 * hRecvReqQueue which is later handled by the Recv thread.
646 *
647 * @param pDrvIns Pointer to PDM driver context.
648 * @param pThread Pointer to calling thread context.
649 *
650 * @returns VBox status code
651 *
652 * @thread NAT
653 */
654static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
655{
656 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
657#ifdef RT_OS_WINDOWS
658 unsigned int cBreak = 0;
659#else /* RT_OS_WINDOWS */
660 unsigned int cPollNegRet = 0;
661 drvNAT_addPollCb(RTPipeToNative(pThis->hPipeRead), SLIRP_POLL_IN | SLIRP_POLL_HUP, pThis);
662 pThis->pNATState->polls[0].fd = RTPipeToNative(pThis->hPipeRead);
663 pThis->pNATState->polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
664 pThis->pNATState->polls[0].revents = 0;
665#endif /* !RT_OS_WINDOWS */
666
667 LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
668
669 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
670 return VINF_SUCCESS;
671
672 if (pThis->enmLinkStateWant != pThis->enmLinkState)
673 drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
674
675 /*
676 * Polling loop.
677 */
678 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
679 {
680 /*
681 * To prevent concurrent execution of sending/receiving threads
682 */
683#ifndef RT_OS_WINDOWS
684 uint32_t uTimeout = 0;
685 pThis->pNATState->nsock = 1;
686
687 slirp_pollfds_fill(pThis->pNATState->pSlirp, &uTimeout, drvNAT_addPollCb /* SlirpAddPollCb */, pThis /* opaque */);
688 slirpUpdateTimeout(&uTimeout, pThis);
689
690 int cChangedFDs = poll(pThis->pNATState->polls, pThis->pNATState->nsock, uTimeout /* timeout */);
691
692 if (cChangedFDs < 0)
693 {
694 if (errno == EINTR)
695 {
696 Log2(("NAT: signal was caught while sleep on poll\n"));
697 /* No error, just process all outstanding requests but don't wait */
698 cChangedFDs = 0;
699 }
700 else if (cPollNegRet++ > 128)
701 {
702 LogRel(("NAT: Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
703 cPollNegRet = 0;
704 }
705 }
706
707
708 slirp_pollfds_poll(pThis->pNATState->pSlirp, cChangedFDs < 0, get_revents_cb /* SlirpGetREventsCb */, pThis /* opaque */);
709 if (pThis->pNATState->polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
710 {
711 /* drain the pipe
712 *
713 * Note! drvNATSend decoupled so we don't know how many times
714 * device's thread sends before we've entered multiplex,
715 * so to avoid false alarm drain pipe here to the very end
716 *
717 * @todo: Probably we should counter drvNATSend to count how
718 * deep pipe has been filed before drain.
719 *
720 */
721 /** @todo XXX: Make it reading exactly we need to drain the
722 * pipe.*/
723 char ch;
724 size_t cbRead;
725 RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
726 }
727
728 /* process _all_ outstanding requests but don't wait */
729 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
730 slirpCheckTimeout(pThis);
731
732#else /* RT_OS_WINDOWS */
733 uint32_t uTimeout = 0;
734 pThis->pNATState->nsock = 0;
735 slirp_pollfds_fill(pThis->pNATState->pSlirp, &uTimeout, drvNAT_addPollCb /* SlirpAddPollCb */, pThis /* opaque */);
736 slirpUpdateTimeout(&uTimeout, pThis);
737
738 int cChangedFDs = WSAPoll(pThis->pNATState->polls, pThis->pNATState->nsock, uTimeout /* timeout */);
739 int error = WSAGetLastError();
740
741 if (cChangedFDs < 0)
742 {
743 LogFlow(("NAT: WSAPoll returned %d (error %d)\n", cChangedFDs, error));
744 LogFlow(("NSOCK = %d\n", pThis->pNATState->nsock));
745
746 if (error == 10022)
747 RTThreadSleep(100);
748 }
749
750 if (cChangedFDs == 0)
751 {
752 /* only check for slow/fast timers */
753 slirp_pollfds_poll(pThis->pNATState->pSlirp, false /*select error*/, get_revents_cb /* SlirpGetREventsCb */, pThis /* opaque */);
754 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
755 continue;
756 }
757 /* poll the sockets in any case */
758 Log2(("%s: poll\n", __FUNCTION__));
759 slirp_pollfds_poll(pThis->pNATState->pSlirp, cChangedFDs < 0 /*select error*/, get_revents_cb /* SlirpGetREventsCb */, pThis /* opaque */);
760
761 /* process _all_ outstanding requests but don't wait */
762 RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
763 slirpCheckTimeout(pThis);
764# ifdef VBOX_NAT_DELAY_HACK
765 if (cBreak++ > 128)
766 {
767 cBreak = 0;
768 RTThreadSleep(2);
769 }
770# endif
771#endif /* RT_OS_WINDOWS */
772 }
773
774 return VINF_SUCCESS;
775}
776
777/**
778 * Unblock the send thread so it can respond to a state change.
779 *
780 * @returns VBox status code.
781 * @param pDrvIns The pcnet device instance.
782 * @param pThread The send thread.
783 *
784 * @thread ?
785 */
786static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
787{
788 RT_NOREF(pThread);
789 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
790
791 drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
792 return VINF_SUCCESS;
793}
794
795/**
796 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
797 */
798static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
799{
800 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
801 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
802
803 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
804 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
805 PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKNATCONFIG, &pThis->INetworkNATCfg);
806 return NULL;
807}
808
809/**
810 * Info handler.
811 *
812 * @param pDrvIns The PDM driver context.
813 * @param pHlp ....
814 * @param pszArgs Unused.
815 *
816 * @thread any
817 */
818static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
819{
820 RT_NOREF(pszArgs);
821 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
822 pHlp->pfnPrintf(pHlp, "libslirp Connection Info:\n");
823 pHlp->pfnPrintf(pHlp, slirp_connection_info(pThis->pNATState->pSlirp));
824 pHlp->pfnPrintf(pHlp, "libslirp Neighbor Info:\n");
825 pHlp->pfnPrintf(pHlp, slirp_neighbor_info(pThis->pNATState->pSlirp));
826 pHlp->pfnPrintf(pHlp, "libslirp Version String: %s \n", slirp_version_string());
827}
828
829/**
830 * Sets up the redirectors.
831 *
832 * @returns VBox status code.
833 * @param pCfg The configuration handle.
834 */
835static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, PRTNETADDRIPV4 pNetwork)
836{
837 PPDMDRVINS pDrvIns = pThis->pDrvIns;
838 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
839
840 RT_NOREF(pNetwork); /** @todo figure why pNetwork isn't used */
841
842 PCFGMNODE pPFTree = pHlp->pfnCFGMGetChild(pCfg, "PortForwarding");
843 if (pPFTree == NULL)
844 return VINF_SUCCESS;
845
846 /*
847 * Enumerate redirections.
848 */
849 for (PCFGMNODE pNode = pHlp->pfnCFGMGetFirstChild(pPFTree); pNode; pNode = pHlp->pfnCFGMGetNextChild(pNode))
850 {
851 /*
852 * Validate the port forwarding config.
853 */
854 if (!pHlp->pfnCFGMAreValuesValid(pNode, "Name\0Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
855 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
856 N_("Unknown configuration in port forwarding"));
857
858 /* protocol type */
859 bool fUDP;
860 char szProtocol[32];
861 int rc;
862 GET_STRING(rc, pDrvIns, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
863 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
864 {
865 fUDP = false;
866 GET_BOOL(rc, pDrvIns, pNode, "UDP", fUDP);
867 }
868 else if (RT_SUCCESS(rc))
869 {
870 if (!RTStrICmp(szProtocol, "TCP"))
871 fUDP = false;
872 else if (!RTStrICmp(szProtocol, "UDP"))
873 fUDP = true;
874 else
875 return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
876 N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
877 iInstance, szProtocol);
878 }
879 else
880 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
881 N_("NAT#%d: configuration query for \"Protocol\" failed"),
882 iInstance);
883 /* host port */
884 int32_t iHostPort;
885 GET_S32_STRICT(rc, pDrvIns, pNode, "HostPort", iHostPort);
886
887 /* guest port */
888 int32_t iGuestPort;
889 GET_S32_STRICT(rc, pDrvIns, pNode, "GuestPort", iGuestPort);
890
891 /* host address ("BindIP" name is rather unfortunate given "HostPort" to go with it) */
892 struct in_addr BindIP;
893 RT_ZERO(BindIP);
894 GETIP_DEF(rc, pDrvIns, pNode, BindIP, INADDR_ANY);
895
896 /* guest address */
897 struct in_addr GuestIP;
898 RT_ZERO(GuestIP);
899 GETIP_DEF(rc, pDrvIns, pNode, GuestIP, INADDR_ANY);
900
901 /*
902 * Call slirp about it.
903 */
904 if (slirp_add_hostfwd(pThis->pNATState->pSlirp, fUDP, BindIP,
905 iHostPort, GuestIP, iGuestPort) < 0)
906 return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
907 N_("NAT#%d: configuration error: failed to set up "
908 "redirection of %d to %d. Probably a conflict with "
909 "existing services or other rules"), iInstance, iHostPort,
910 iGuestPort);
911 } /* for each redir rule */
912
913 return VINF_SUCCESS;
914}
915
916static DECLCALLBACK(void) drvNATNotifyApplyPortForwardCommand(PDRVNAT pThis, bool fRemove,
917 bool fUdp, const char *pHostIp,
918 uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
919{
920 struct in_addr guestIp, hostIp;
921
922 if ( pHostIp == NULL
923 || inet_aton(pHostIp, &hostIp) == 0)
924 hostIp.s_addr = INADDR_ANY;
925
926 if ( pGuestIp == NULL
927 || inet_aton(pGuestIp, &guestIp) == 0)
928 guestIp.s_addr = pThis->GuestIP;
929
930 if (fRemove)
931 slirp_remove_hostfwd(pThis->pNATState->pSlirp, fUdp, hostIp, u16HostPort);
932 else
933 slirp_add_hostfwd(pThis->pNATState->pSlirp, fUdp, hostIp,
934 u16HostPort, guestIp, u16GuestPort);
935}
936
937static DECLCALLBACK(int) drvNATNetworkNatConfigRedirect(PPDMINETWORKNATCONFIG pInterface, bool fRemove,
938 bool fUdp, const char *pHostIp, uint16_t u16HostPort,
939 const char *pGuestIp, uint16_t u16GuestPort)
940{
941 LogFlowFunc(("fRemove=%d, fUdp=%d, pHostIp=%s, u16HostPort=%u, pGuestIp=%s, u16GuestPort=%u\n",
942 RT_BOOL(fRemove), RT_BOOL(fUdp), pHostIp, u16HostPort, pGuestIp, u16GuestPort));
943 PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
944 /* Execute the command directly if the VM is not running. */
945 int rc;
946 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
947 {
948 drvNATNotifyApplyPortForwardCommand(pThis, fRemove, fUdp, pHostIp,
949 u16HostPort, pGuestIp,u16GuestPort);
950 rc = VINF_SUCCESS;
951 }
952 else
953 {
954 PRTREQ pReq;
955 rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
956 (PFNRT)drvNATNotifyApplyPortForwardCommand, 7, pThis, fRemove,
957 fUdp, pHostIp, u16HostPort, pGuestIp, u16GuestPort);
958 if (rc == VERR_TIMEOUT)
959 {
960 drvNATNotifyNATThread(pThis, "drvNATNetworkNatConfigRedirect");
961 rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
962 AssertRC(rc);
963 }
964 else
965 AssertRC(rc);
966
967 RTReqRelease(pReq);
968 }
969 return rc;
970}
971
972static void slirpUpdateTimeout(uint32_t *uTimeout, void *opaque)
973{
974 PDRVNAT pThis = (PDRVNAT)opaque;
975 Assert(pThis);
976
977 int64_t currTime = slirpClockGetNsCb(pThis) / (1000 * 1000);
978 SlirpTimer *pCurrent = pThis->pNATState->pTimerHead;
979 while (pCurrent != NULL)
980 {
981 if (pCurrent->uTimeExpire != -1)
982 {
983 int64_t diff = pCurrent->uTimeExpire - currTime;
984
985 if (diff < 0)
986 diff = 0;
987
988 if (diff < *uTimeout)
989 *uTimeout = diff;
990 }
991
992 pCurrent = pCurrent->next;
993 }
994}
995
996static void slirpCheckTimeout(void *opaque)
997{
998 PDRVNAT pThis = (PDRVNAT)opaque;
999 Assert(pThis);
1000
1001 int64_t currTime = slirpClockGetNsCb(pThis) / (1000 * 1000);
1002 SlirpTimer *pCurrent = pThis->pNATState->pTimerHead;
1003 while (pCurrent != NULL)
1004 {
1005 if (pCurrent->uTimeExpire != -1)
1006 {
1007 int64_t diff = pCurrent->uTimeExpire - currTime;
1008 if (diff <= 0)
1009 {
1010 pCurrent->uTimeExpire = -1;
1011 pCurrent->pHandler(pCurrent->opaque);
1012 }
1013 }
1014
1015 pCurrent = pCurrent->next;
1016 }
1017}
1018
1019/**
1020 * CALLBACKS
1021 */
1022static DECLCALLBACK(ssize_t) slirpSendPacketCb(const void *pBuf, size_t cb, void *opaque /* PDRVNAT */)
1023{
1024 char *pNewBuf = (char *)RTMemAlloc(cb);
1025 memcpy(pNewBuf, pBuf, cb);
1026
1027 PDRVNAT pThis = (PDRVNAT)opaque;
1028 Assert(pThis);
1029
1030 LogFlow(("slirp_output BEGIN %p %d\n", pNewBuf, cb));
1031 Log6(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pNewBuf, cb, pThis));
1032
1033 /* don't queue new requests when the NAT thread is about to stop */
1034 if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
1035 return -1;
1036
1037 ASMAtomicIncU32(&pThis->cPkts);
1038 int rc = RTReqQueueCallEx(pThis->hRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1039 (PFNRT)drvNATRecvWorker, 3, pThis, pNewBuf, cb);
1040 AssertRC(rc);
1041 drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
1042 drvNATNotifyNATThread(pThis, "slirpSendPacketCb");
1043 STAM_COUNTER_INC(&pThis->StatQueuePktSent);
1044 LogFlowFuncLeave();
1045 return cb;
1046}
1047
1048static DECLCALLBACK(void) slirpGuestErrorCb(const char *pMsg, void *opaque)
1049{
1050 PDRVNAT pThis = (PDRVNAT)opaque;
1051 Assert(pThis);
1052
1053 PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_UNKNOWN_DRVREG_VERSION,
1054 N_("Unknown error: "));
1055 LogRel((pMsg));
1056}
1057
1058static DECLCALLBACK(int64_t) slirpClockGetNsCb(void *opaque)
1059{
1060 PDRVNAT pThis = (PDRVNAT)opaque;
1061 Assert(pThis);
1062
1063 return (int64_t)RTTimeNanoTS();
1064}
1065
1066static DECLCALLBACK(void *) slirpTimerNewCb(SlirpTimerCb slirpTimeCb, void *cb_opaque, void *opaque)
1067{
1068 PDRVNAT pThis = (PDRVNAT)opaque;
1069 Assert(pThis);
1070
1071 SlirpTimer *pNewTimer = (SlirpTimer *)RTMemAlloc(sizeof(SlirpTimer));
1072 if (!pNewTimer)
1073 return NULL;
1074
1075 pNewTimer->next = pThis->pNATState->pTimerHead;
1076 pNewTimer->uTimeExpire = -1;
1077 pNewTimer->pHandler = slirpTimeCb;
1078 pNewTimer->opaque = cb_opaque;
1079 pThis->pNATState->pTimerHead = pNewTimer;
1080
1081 return pNewTimer;
1082}
1083
1084static DECLCALLBACK(void) slirpTimerFreeCb(void *pTimer, void *opaque)
1085{
1086 PDRVNAT pThis = (PDRVNAT)opaque;
1087 Assert(pThis);
1088 SlirpTimer *pCurrent = pThis->pNATState->pTimerHead;
1089
1090 while (pCurrent != NULL)
1091 {
1092 if (pCurrent == (SlirpTimer *)pTimer)
1093 {
1094 SlirpTimer *pTmp = pCurrent->next;
1095 RTMemFree(pCurrent);
1096 pCurrent = pTmp;
1097 }
1098 else
1099 pCurrent = pCurrent->next;
1100 }
1101}
1102
1103static DECLCALLBACK(void) slirpTimerModCb(void *pTimer, int64_t expireTime, void *opaque)
1104{
1105 PDRVNAT pThis = (PDRVNAT)opaque;
1106 Assert(pThis);
1107
1108 ((SlirpTimer *)pTimer)->uTimeExpire = expireTime;
1109}
1110
1111static DECLCALLBACK(void) slirpNotifyCb(void *opaque)
1112{
1113 PDRVNAT pThis = (PDRVNAT)opaque;
1114
1115 drvNATAsyncIoWakeup(pThis->pDrvIns, NULL);
1116}
1117
1118/**
1119 * Destruct a driver instance.
1120 *
1121 * Most VM resources are freed by the VM. This callback is provided so that any non-VM
1122 * resources can be freed correctly.
1123 *
1124 * @param pDrvIns The driver instance data.
1125 */
1126static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
1127{
1128 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1129 LogFlow(("drvNATDestruct:\n"));
1130 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1131
1132 if (pThis->pNATState)
1133 {
1134 slirp_cleanup(pThis->pNATState->pSlirp);
1135#ifdef VBOX_WITH_STATISTICS
1136# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1137# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
1138# include "slirp/counters.h"
1139#endif
1140 pThis->pNATState = NULL;
1141 }
1142
1143 RTReqQueueDestroy(pThis->hSlirpReqQueue);
1144 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1145
1146 RTReqQueueDestroy(pThis->hUrgRecvReqQueue);
1147 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1148
1149 RTReqQueueDestroy(pThis->hRecvReqQueue);
1150 pThis->hRecvReqQueue = NIL_RTREQQUEUE;
1151
1152 RTSemEventDestroy(pThis->EventRecv);
1153 pThis->EventRecv = NIL_RTSEMEVENT;
1154
1155 RTSemEventDestroy(pThis->EventUrgRecv);
1156 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1157
1158 if (RTCritSectIsInitialized(&pThis->DevAccessLock))
1159 RTCritSectDelete(&pThis->DevAccessLock);
1160
1161 if (RTCritSectIsInitialized(&pThis->XmitLock))
1162 RTCritSectDelete(&pThis->XmitLock);
1163
1164#ifndef RT_OS_WINDOWS
1165 RTPipeClose(pThis->hPipeRead);
1166 RTPipeClose(pThis->hPipeWrite);
1167#endif
1168
1169#ifdef RT_OS_DARWIN
1170 /* Cleanup the DNS watcher. */
1171 if (pThis->hRunLoopSrcDnsWatcher != NULL)
1172 {
1173 CFRunLoopRef hRunLoopMain = CFRunLoopGetMain();
1174 CFRetain(hRunLoopMain);
1175 CFRunLoopRemoveSource(hRunLoopMain, pThis->hRunLoopSrcDnsWatcher, kCFRunLoopCommonModes);
1176 CFRelease(hRunLoopMain);
1177 CFRelease(pThis->hRunLoopSrcDnsWatcher);
1178 pThis->hRunLoopSrcDnsWatcher = NULL;
1179 }
1180#endif
1181}
1182
1183/**
1184 * Construct a NAT network transport driver instance.
1185 *
1186 * @copydoc FNPDMDRVCONSTRUCT
1187 */
1188static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1189{
1190 int rc = 0;
1191
1192 /* Construct PDRVNAT */
1193
1194 RT_NOREF(fFlags);
1195 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1196 PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
1197
1198 /*
1199 * Init the static parts.
1200 */
1201 pThis->pDrvIns = pDrvIns;
1202 pThis->pNATState = (SlirpState *)RTMemAlloc(sizeof(SlirpState));
1203 if(pThis->pNATState == NULL)
1204 {
1205 return VERR_NO_MEMORY;
1206 }
1207 else
1208 {
1209 pThis->pNATState->nsock = 0;
1210 pThis->pNATState->pTimerHead = NULL;
1211 pThis->pNATState->polls = (struct pollfd *)RTMemAlloc(64 * sizeof(struct pollfd));
1212 pThis->pNATState->uPollCap = 64;
1213 }
1214 pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
1215 pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
1216 pThis->EventRecv = NIL_RTSEMEVENT;
1217 pThis->EventUrgRecv = NIL_RTSEMEVENT;
1218#ifdef RT_OS_DARWIN
1219 pThis->hRunLoopSrcDnsWatcher = NULL;
1220#endif
1221
1222 /* IBase */
1223 pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
1224
1225 /* INetwork */
1226 pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
1227 pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
1228 pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
1229 pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
1230 pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
1231 pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
1232 pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
1233
1234 /* NAT engine configuration */
1235 pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfigRedirect;
1236 pThis->INetworkNATCfg.pfnNotifyDnsChanged = NULL;
1237
1238 /*
1239 * Validate the config.
1240 */
1241 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
1242 "PassDomain"
1243 "|TFTPPrefix"
1244 "|BootFile"
1245 "|Network"
1246 "|NextServer"
1247 "|DNSProxy"
1248 "|BindIP"
1249 "|UseHostResolver"
1250 "|SlirpMTU"
1251 "|AliasMode"
1252 "|SockRcv"
1253 "|SockSnd"
1254 "|TcpRcv"
1255 "|TcpSnd"
1256 "|ICMPCacheLimit"
1257 "|SoMaxConnection"
1258 "|LocalhostReachable"
1259 "|HostResolverMappings"
1260 , "PortForwarding");
1261
1262 /*
1263 * Get the configuration settings.
1264 */
1265 bool fPassDomain = true;
1266 GET_BOOL(rc, pDrvIns, pCfg, "PassDomain", fPassDomain);
1267
1268 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
1269 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "BootFile", pThis->pszBootFile);
1270 GET_STRING_ALLOC(rc, pDrvIns, pCfg, "NextServer", pThis->pszNextServer);
1271
1272 int fDNSProxy = 0;
1273 GET_S32(rc, pDrvIns, pCfg, "DNSProxy", fDNSProxy);
1274 int MTU = 1500;
1275 GET_S32(rc, pDrvIns, pCfg, "SlirpMTU", MTU);
1276 int i32AliasMode = 0;
1277 int i32MainAliasMode = 0;
1278 GET_S32(rc, pDrvIns, pCfg, "AliasMode", i32MainAliasMode);
1279 int iIcmpCacheLimit = 100;
1280 GET_S32(rc, pDrvIns, pCfg, "ICMPCacheLimit", iIcmpCacheLimit);
1281 bool fLocalhostReachable = false;
1282 GET_BOOL(rc, pDrvIns, pCfg, "LocalhostReachable", fLocalhostReachable);
1283
1284 i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
1285 i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
1286 i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
1287 int i32SoMaxConn = 10;
1288 GET_S32(rc, pDrvIns, pCfg, "SoMaxConnection", i32SoMaxConn);
1289 /*
1290 * Query the network port interface.
1291 */
1292 pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
1293 if (!pThis->pIAboveNet)
1294 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1295 N_("Configuration error: the above device/driver didn't "
1296 "export the network port interface"));
1297 pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
1298 if (!pThis->pIAboveConfig)
1299 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
1300 N_("Configuration error: the above device/driver didn't "
1301 "export the network config interface"));
1302
1303 /* Generate a network address for this network card. */
1304 char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
1305 GET_STRING(rc, pDrvIns, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
1306 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
1307 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: missing network"),
1308 pDrvIns->iInstance);
1309
1310 RTNETADDRIPV4 Network, Netmask;
1311
1312 rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
1313 if (RT_FAILURE(rc))
1314 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1315 N_("NAT#%d: Configuration error: network '%s' describes not a valid IPv4 network"),
1316 pDrvIns->iInstance, szNetwork);
1317
1318 /* Construct Libslirp Config and Initialzie Slirp */
1319
1320 LogFlow(("Here is what is coming out of the vbox config:\n \
1321 Network: %lu\n \
1322 Netmask: %lu\n", Network, Netmask));
1323
1324#ifndef RT_OS_WINDOWS
1325 struct in_addr vnetwork = RTNetIPv4AddrHEToInAddr(&Network);
1326 struct in_addr vnetmask = RTNetIPv4AddrHEToInAddr(&Netmask);
1327 struct in_addr vhost = RTNetInAddrFromU8(10, 0, 2, 2);
1328 struct in_addr vdhcp_start = RTNetInAddrFromU8(10, 0, 2, 15);
1329 struct in_addr vnameserver = RTNetInAddrFromU8(10, 0, 2, 3);
1330#else
1331 struct in_addr vnetwork;
1332 vnetwork.S_un.S_addr = RT_BSWAP_U32(Network.u);
1333
1334 struct in_addr vnetmask;
1335 vnetmask.S_un.S_addr = RT_BSWAP_U32(Netmask.u);
1336
1337 struct in_addr vhost;
1338 vhost.S_un.S_addr = RT_BSWAP_U32(0x0a000202);
1339
1340 struct in_addr vdhcp_start;
1341 vdhcp_start.S_un.S_addr = RT_BSWAP_U32(0x0a00020f);
1342
1343 struct in_addr vnameserver;
1344 vnameserver.S_un.S_addr = RT_BSWAP_U32(0x0a000203);
1345#endif
1346
1347 SlirpConfig *pSlirpCfg = new SlirpConfig { 0 };
1348
1349 pSlirpCfg->version = 4;
1350 pSlirpCfg->restricted = false;
1351 pSlirpCfg->in_enabled = true;
1352 pSlirpCfg->vnetwork = vnetwork;
1353 pSlirpCfg->vnetmask = vnetmask;
1354 pSlirpCfg->vhost = vhost;
1355 pSlirpCfg->in6_enabled = true;
1356
1357 inet_pton(AF_INET6, "fd00::", &pSlirpCfg->vprefix_addr6);
1358 pSlirpCfg->vprefix_len = 64;
1359 inet_pton(AF_INET6, "fd00::2", &pSlirpCfg->vhost6);
1360
1361 pSlirpCfg->vhostname = "vbox";
1362 pSlirpCfg->tftp_server_name = pThis->pszNextServer;
1363 pSlirpCfg->tftp_path = pThis->pszTFTPPrefix;
1364 pSlirpCfg->bootfile = pThis->pszBootFile;
1365 pSlirpCfg->vdhcp_start = vdhcp_start;
1366 pSlirpCfg->vnameserver = vnameserver;
1367 pSlirpCfg->if_mtu = MTU;
1368
1369#ifndef RT_OS_WINDOWS
1370 inet_pton(AF_INET6, "fd00::3", &pSlirpCfg->vnameserver6);
1371#else
1372 inet_pton(23, "fd00::3", &pSlirpCfg->vnameserver6);
1373#endif
1374
1375 pSlirpCfg->vdnssearch = NULL;
1376 pSlirpCfg->vdomainname = NULL;
1377
1378 SlirpCb *slirpCallbacks = (struct SlirpCb *)RTMemAlloc(sizeof(SlirpCb));
1379
1380 slirpCallbacks->send_packet = &slirpSendPacketCb;
1381 slirpCallbacks->guest_error = &slirpGuestErrorCb;
1382 slirpCallbacks->clock_get_ns = &slirpClockGetNsCb;
1383 slirpCallbacks->timer_new = &slirpTimerNewCb;
1384 slirpCallbacks->timer_free = &slirpTimerFreeCb;
1385 slirpCallbacks->timer_mod = &slirpTimerModCb;
1386 slirpCallbacks->register_poll_fd = &drvNAT_RegisterPoll;
1387 slirpCallbacks->unregister_poll_fd = &drvNAT_UnregisterPoll;
1388 slirpCallbacks->notify = &slirpNotifyCb;
1389 slirpCallbacks->init_completed = NULL;
1390 slirpCallbacks->timer_new_opaque = NULL;
1391
1392 Slirp *pSlirp = slirp_new(/* cfg */ pSlirpCfg, /* callbacks */ slirpCallbacks, /* opaque */ pThis);
1393
1394 if (pSlirp == NULL)
1395 return VERR_INVALID_POINTER;
1396
1397 pThis->pNATState->pSlirp = pSlirp;
1398
1399 // pThis->pNATState->polls = NULL;
1400
1401 rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, &Network);
1402 AssertLogRelRCReturn(rc, rc);
1403
1404 rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, NULL);
1405 AssertLogRelRCReturn(rc, rc);
1406
1407 rc = RTReqQueueCreate(&pThis->hSlirpReqQueue);
1408 AssertLogRelRCReturn(rc, rc);
1409
1410 rc = RTReqQueueCreate(&pThis->hRecvReqQueue);
1411 AssertLogRelRCReturn(rc, rc);
1412
1413 rc = RTReqQueueCreate(&pThis->hUrgRecvReqQueue);
1414 AssertLogRelRCReturn(rc, rc);
1415
1416 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
1417 drvNATRecvWakeup, 256 * _1K, RTTHREADTYPE_IO, "NATRX");
1418 AssertRCReturn(rc, rc);
1419
1420 rc = RTSemEventCreate(&pThis->EventRecv);
1421 AssertRCReturn(rc, rc);
1422
1423 rc = RTSemEventCreate(&pThis->EventUrgRecv);
1424 AssertRCReturn(rc, rc);
1425
1426 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
1427 drvNATUrgRecvWakeup, 256 * _1K, RTTHREADTYPE_IO, "NATURGRX");
1428 AssertRCReturn(rc, rc);
1429
1430 rc = RTCritSectInit(&pThis->DevAccessLock);
1431 AssertRCReturn(rc, rc);
1432
1433 rc = RTCritSectInit(&pThis->XmitLock);
1434 AssertRCReturn(rc, rc);
1435
1436 char szTmp[128];
1437 RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
1438 PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
1439
1440#ifdef VBOX_WITH_STATISTICS
1441# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
1442# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
1443# include "slirp/counters.h"
1444#endif
1445
1446#ifndef RT_OS_WINDOWS
1447 /*
1448 * Create the control pipe.
1449 */
1450 rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
1451 AssertRCReturn(rc, rc);
1452#else
1453 pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
1454 pThis->pNATState->phEvents[VBOX_WAKEUP_EVENT_INDEX] = pThis->hWakeupEvent;
1455 pThis->pNATState->phEvents[VBOX_SOCKET_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
1456#endif
1457
1458 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
1459 drvNATAsyncIoWakeup, 256 * _1K, RTTHREADTYPE_IO, "NAT");
1460 AssertRCReturn(rc, rc);
1461
1462 pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
1463
1464 return rc;
1465}
1466
1467/**
1468 * NAT network transport driver registration record.
1469 */
1470const PDMDRVREG g_DrvNATlibslirp =
1471{
1472 /* u32Version */
1473 PDM_DRVREG_VERSION,
1474 /* szName */
1475 "NAT",
1476 /* szRCMod */
1477 "",
1478 /* szR0Mod */
1479 "",
1480 /* pszDescription */
1481 "NATlibslrip Network Transport Driver",
1482 /* fFlags */
1483 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1484 /* fClass. */
1485 PDM_DRVREG_CLASS_NETWORK,
1486 /* cMaxInstances */
1487 ~0U,
1488 /* cbInstance */
1489 sizeof(DRVNAT),
1490 /* pfnConstruct */
1491 drvNATConstruct,
1492 /* pfnDestruct */
1493 drvNATDestruct,
1494 /* pfnRelocate */
1495 NULL,
1496 /* pfnIOCtl */
1497 NULL,
1498 /* pfnPowerOn */
1499 NULL,
1500 /* pfnReset */
1501 NULL,
1502 /* pfnSuspend */
1503 NULL,
1504 /* pfnResume */
1505 NULL,
1506 /* pfnAttach */
1507 NULL,
1508 /* pfnDetach */
1509 NULL,
1510 /* pfnPowerOff */
1511 NULL,
1512 /* pfnSoftReset */
1513 NULL,
1514 /* u32EndVersion */
1515 PDM_DRVREG_VERSION
1516};
Note: See TracBrowser for help on using the repository browser.

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