VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/VBoxNetSlirpNAT.cpp@ 109108

Last change on this file since 109108 was 109108, checked in by vboxsync, 3 weeks ago

NetworkServices/NAT/VBoxNetSlirpNAT.cpp: Restore support for port forwarding, bugref:10268 [doxygen]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.3 KB
Line 
1/* $Id: VBoxNetSlirpNAT.cpp 109108 2025-04-29 13:29:31Z vboxsync $ */
2/** @file
3 * VBoxNetNAT - NAT Service for connecting to IntNet.
4 */
5
6/*
7 * Copyright (C) 2009-2025 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
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#define LOG_GROUP LOG_GROUP_NAT_SERVICE
29
30#ifdef RT_OS_WINDOWS
31# include <iprt/win/winsock2.h>
32# include <iprt/win/ws2tcpip.h>
33# include "winutils.h"
34# define inet_aton(x, y) inet_pton(2, x, y)
35# define AF_INET6 23
36#endif
37
38#include <VBox/com/assert.h>
39#include <VBox/com/com.h>
40#include <VBox/com/listeners.h>
41#include <VBox/com/string.h>
42#include <VBox/com/Guid.h>
43#include <VBox/com/array.h>
44#include <VBox/com/ErrorInfo.h>
45#include <VBox/com/errorprint.h>
46#include <VBox/com/VirtualBox.h>
47#include <VBox/com/NativeEventQueue.h>
48
49#include <iprt/net.h>
50#include <iprt/initterm.h>
51#include <iprt/alloca.h>
52#ifndef RT_OS_WINDOWS
53# include <arpa/inet.h>
54#endif
55#include <iprt/err.h>
56#include <iprt/time.h>
57#include <iprt/timer.h>
58#include <iprt/thread.h>
59#include <iprt/stream.h>
60#include <iprt/path.h>
61#include <iprt/param.h>
62#include <iprt/pipe.h>
63#include <iprt/string.h>
64#include <iprt/mem.h>
65#include <iprt/message.h>
66#include <iprt/req.h>
67#include <iprt/file.h>
68#include <iprt/semaphore.h>
69#include <iprt/cpp/utils.h>
70#include <VBox/log.h>
71
72#include <iprt/buildconfig.h>
73#include <iprt/getopt.h>
74#include <iprt/process.h>
75
76#include <VBox/sup.h>
77#include <VBox/intnet.h>
78#include <VBox/intnetinline.h>
79#include <VBox/vmm/pdmnetinline.h>
80#include <VBox/vmm/vmm.h>
81#include <VBox/version.h>
82
83#ifndef RT_OS_WINDOWS
84# include <sys/poll.h>
85# include <sys/socket.h>
86# include <netinet/in.h>
87# ifdef RT_OS_LINUX
88# include <linux/icmp.h> /* ICMP_FILTER */
89# endif
90# include <netinet/icmp6.h>
91#endif
92
93#include <map>
94#include <vector>
95#include <iprt/sanitized/string>
96
97#include <stdio.h>
98
99#include "../NetLib/IntNetIf.h"
100#include "../NetLib/VBoxPortForwardString.h"
101
102#include <libslirp.h>
103
104#ifdef VBOX_RAWSOCK_DEBUG_HELPER
105#if defined(VBOX_WITH_HARDENING) /* obviously */ \
106 || defined(RT_OS_WINDOWS) /* not used */ \
107 || defined(RT_OS_DARWIN) /* not necessary */
108# error Have you forgotten to turn off VBOX_RAWSOCK_DEBUG_HELPER?
109#endif
110/* ask the privileged helper to create a raw socket for us */
111extern "C" int getrawsock(int type);
112#endif
113
114/** The maximum (default) poll/WSAPoll timeout. */
115#define DRVNAT_DEFAULT_TIMEOUT (int)RT_MS_1HOUR
116
117
118typedef struct NATSERVICEPORTFORWARDRULE
119{
120 PORTFORWARDRULE Pfr;
121} NATSERVICEPORTFORWARDRULE, *PNATSERVICEPORTFORWARDRULE;
122
123typedef std::vector<NATSERVICEPORTFORWARDRULE> VECNATSERVICEPF;
124typedef VECNATSERVICEPF::iterator ITERATORNATSERVICEPF;
125typedef VECNATSERVICEPF::const_iterator CITERATORNATSERVICEPF;
126
127
128/** Slirp Timer */
129typedef struct slirpTimer
130{
131 struct slirpTimer *next;
132 /** The time deadline (milliseconds, RTTimeMilliTS). */
133 int64_t msExpire;
134 SlirpTimerCb pHandler;
135 void *opaque;
136} SlirpTimer;
137
138
139class VBoxNetSlirpNAT
140{
141 static RTGETOPTDEF s_aGetOptDef[];
142
143 com::Utf8Str m_strNetworkName;
144 int m_uVerbosity;
145
146 ComPtr<IVirtualBoxClient> virtualboxClient;
147 ComPtr<IVirtualBox> virtualbox;
148 ComPtr<IHost> m_host;
149 ComPtr<INATNetwork> m_net;
150
151 RTMAC m_MacAddress;
152 INTNETIFCTX m_hIf;
153 RTTHREAD m_hThrRecv;
154 RTTHREAD m_hThrdPoll;
155 /** Queue for NAT-thread-external events. */
156 RTREQQUEUE m_hSlirpReqQueue;
157
158 /** Home folder location; used as default directory for several paths. */
159 com::Utf8Str m_strHome;
160
161#ifdef RT_OS_WINDOWS
162 /** Wakeup socket pair for NAT thread.
163 * Entry #0 is write, entry #1 is read. */
164 SOCKET m_ahWakeupSockPair[2];
165#else
166 /** The write end of the control pipe. */
167 RTPIPE m_hPipeWrite;
168 /** The read end of the control pipe. */
169 RTPIPE m_hPipeRead;
170#endif
171
172 volatile uint64_t m_cWakeupNotifs;
173
174 SlirpConfig m_ProxyOptions;
175 struct sockaddr_in m_src4;
176 struct sockaddr_in6 m_src6;
177
178 uint16_t m_u16Mtu;
179
180 unsigned int nsock;
181
182 Slirp *m_pSlirp;
183 struct pollfd *polls;
184
185 /** Num Polls (not bytes) */
186 unsigned int uPollCap = 0;
187
188 /** List of timers (in reverse creation order).
189 * @note There is currently only one libslirp timer (v4.8 / 2025-01-16). */
190 SlirpTimer *pTimerHead;
191 bool fPassDomain;
192
193 VECNATSERVICEPF m_vecPortForwardRule4;
194 VECNATSERVICEPF m_vecPortForwardRule6;
195
196 class Listener
197 {
198 class Adapter;
199 typedef ListenerImpl<Adapter, VBoxNetSlirpNAT *> Impl;
200
201 ComObjPtr<Impl> m_pListenerImpl;
202 ComPtr<IEventSource> m_pEventSource;
203
204 public:
205 HRESULT init(VBoxNetSlirpNAT *pNAT);
206 void uninit();
207
208 template <typename IEventful>
209 HRESULT listen(const ComPtr<IEventful> &pEventful,
210 const VBoxEventType_T aEvents[]);
211 HRESULT unlisten();
212
213 private:
214 HRESULT doListen(const ComPtr<IEventSource> &pEventSource,
215 const VBoxEventType_T aEvents[]);
216 };
217
218 Listener m_ListenerNATNet;
219 Listener m_ListenerVirtualBox;
220 Listener m_ListenerVBoxClient;
221
222public:
223 VBoxNetSlirpNAT();
224 ~VBoxNetSlirpNAT();
225
226 RTEXITCODE parseArgs(int argc, char *argv[]);
227
228 int init();
229 int run();
230 void shutdown();
231
232private:
233 RTEXITCODE usage();
234
235 int initCom();
236 int initHome();
237 int initLog();
238 int initIPv4();
239 int initIPv4LoopbackMap();
240 int initIPv6();
241 int initComEvents();
242
243 int getExtraData(com::Utf8Str &strValueOut, const char *pcszKey);
244 void timersRunExpired(void);
245
246 static void reportError(const char *a_pcszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
247
248 static HRESULT reportComError(ComPtr<IUnknown> iface,
249 const com::Utf8Str &strContext,
250 HRESULT hrc);
251 static void reportErrorInfoList(const com::ErrorInfo &info,
252 const com::Utf8Str &strContext);
253 static void reportErrorInfo(const com::ErrorInfo &info);
254
255#if 0
256 void initIPv4RawSock();
257 void initIPv6RawSock();
258#endif
259
260 HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent);
261
262 const char **getHostNameservers();
263
264 int slirpTimersAdjustTimeoutDown(int cMsTimeout);
265 void slirpNotifyPollThread(const char *pszWho);
266
267 int fetchNatPortForwardRules(VECNATSERVICEPF &vec, bool fIsIPv6);
268 int natServiceProcessRegisteredPf(VECNATSERVICEPF &vecPf);
269
270 static DECLCALLBACK(void) natServicePfRegister(VBoxNetSlirpNAT *pThis, PPORTFORWARDRULE pNatPf, bool fRemove, bool fRuntime);
271 static DECLCALLBACK(int) pollThread(RTTHREAD hThreadSelf, void *pvUser);
272 static DECLCALLBACK(int) receiveThread(RTTHREAD hThreadSelf, void *pvUser);
273
274 /* input from intnet */
275 static DECLCALLBACK(void) processFrame(void *pvUser, void *pvFrame, uint32_t cbFrame);
276
277 static ssize_t slirpSendPacketCb(const void *pvBuf, ssize_t cb, void *pvUser) RT_NOTHROW_PROTO;
278 static void slirpGuestErrorCb(const char *pszMsg, void *pvUser) RT_NOTHROW_PROTO;
279 static int64_t slirpClockGetNsCb(void *pvUser) RT_NOTHROW_PROTO;
280 static void *slirpTimerNewCb(SlirpTimerCb slirpTimeCb, void *cb_opaque, void *opaque) RT_NOTHROW_PROTO;
281 static void slirpTimerFreeCb(void *pvTimer, void *pvUser) RT_NOTHROW_PROTO;
282 static void slirpTimerModCb(void *pvTimer, int64_t msNewDeadlineTs, void *pvUser) RT_NOTHROW_PROTO;
283 static void slirpNotifyCb(void *opaque) RT_NOTHROW_PROTO;
284 static void slirpRegisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_PROTO;
285 static void slirpUnregisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_PROTO;
286 static int slirpAddPollCb(slirp_os_socket hFd, int iEvents, void *opaque) RT_NOTHROW_PROTO;
287 static int slirpGetREventsCb(int idx, void *opaque) RT_NOTHROW_PROTO;
288
289 static DECLCALLBACK(void) slirpSendWorker(VBoxNetSlirpNAT *pThis, void *pvFrame, size_t cbFrame);
290};
291
292
293
294VBoxNetSlirpNAT::VBoxNetSlirpNAT()
295 : m_uVerbosity(0),
296 m_hIf(NULL),
297 m_hThrRecv(NIL_RTTHREAD),
298 m_hThrdPoll(NIL_RTTHREAD),
299 m_hSlirpReqQueue(NIL_RTREQQUEUE),
300 m_cWakeupNotifs(0),
301 m_u16Mtu(1500)
302{
303 LogFlowFuncEnter();
304
305#if 0
306 RT_ZERO(m_ProxyOptions.ipv4_addr);
307 RT_ZERO(m_ProxyOptions.ipv4_mask);
308 RT_ZERO(m_ProxyOptions.ipv6_addr);
309 m_ProxyOptions.ipv6_enabled = 0;
310 m_ProxyOptions.ipv6_defroute = 0;
311 m_ProxyOptions.icmpsock4 = INVALID_SOCKET;
312 m_ProxyOptions.icmpsock6 = INVALID_SOCKET;
313 m_ProxyOptions.tftp_root = NULL;
314 m_ProxyOptions.src4 = NULL;
315 m_ProxyOptions.src6 = NULL;
316#endif
317 RT_ZERO(m_src4);
318 RT_ZERO(m_src6);
319 m_src4.sin_family = AF_INET;
320 m_src6.sin6_family = AF_INET6;
321#ifdef HAVE_SA_LEN
322 m_src4.sin_len = sizeof(m_src4);
323 m_src6.sin6_len = sizeof(m_src6);
324#endif
325
326 pTimerHead = NULL;
327 nsock = 0;
328
329 polls = (struct pollfd *)RTMemAllocZ(64 * sizeof(struct pollfd));
330 uPollCap = 64;
331
332 m_MacAddress.au8[0] = 0x52;
333 m_MacAddress.au8[1] = 0x54;
334 m_MacAddress.au8[2] = 0;
335 m_MacAddress.au8[3] = 0x12;
336 m_MacAddress.au8[4] = 0x35;
337 m_MacAddress.au8[5] = 0;
338
339 LogFlowFuncLeave();
340}
341
342
343VBoxNetSlirpNAT::~VBoxNetSlirpNAT()
344{
345 RTReqQueueDestroy(m_hSlirpReqQueue);
346 m_hSlirpReqQueue = NIL_RTREQQUEUE;
347
348#if 0
349 if (m_ProxyOptions.tftp_root)
350 {
351 RTStrFree((char *)m_ProxyOptions.tftp_root);
352 m_ProxyOptions.tftp_root = NULL;
353 }
354 if (m_ProxyOptions.nameservers)
355 {
356 const char **pv = m_ProxyOptions.nameservers;
357 while (*pv)
358 {
359 RTStrFree((char*)*pv);
360 pv++;
361 }
362 RTMemFree(m_ProxyOptions.nameservers);
363 m_ProxyOptions.nameservers = NULL;
364 }
365#endif
366}
367
368
369/**
370 * Command line options.
371 */
372RTGETOPTDEF VBoxNetSlirpNAT::s_aGetOptDef[] =
373{
374 { "--network", 'n', RTGETOPT_REQ_STRING },
375 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
376};
377
378
379/** Icky hack to tell the caller it should exit with RTEXITCODE_SUCCESS */
380#define RTEXITCODE_DONE RTEXITCODE_32BIT_HACK
381
382RTEXITCODE
383VBoxNetSlirpNAT::usage()
384{
385 RTPrintf("%s Version %sr%u\n"
386 "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
387 "\n"
388 "Usage: %s <options>\n"
389 "\n"
390 "Options:\n",
391 RTProcShortName(), RTBldCfgVersion(), RTBldCfgRevision(),
392 RTProcShortName());
393 for (size_t i = 0; i < RT_ELEMENTS(s_aGetOptDef); ++i)
394 RTPrintf(" -%c, %s\n", s_aGetOptDef[i].iShort, s_aGetOptDef[i].pszLong);
395
396 return RTEXITCODE_DONE;
397}
398
399
400RTEXITCODE
401VBoxNetSlirpNAT::parseArgs(int argc, char *argv[])
402{
403 unsigned int uVerbosity = 0;
404
405 RTGETOPTSTATE State;
406 int rc = RTGetOptInit(&State, argc, argv,
407 s_aGetOptDef, RT_ELEMENTS(s_aGetOptDef),
408 1, 0);
409 if (RT_FAILURE(rc))
410 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
411
412 int ch;
413 RTGETOPTUNION Val;
414 while ((ch = RTGetOpt(&State, &Val)) != 0)
415 {
416 switch (ch)
417 {
418 case 'n': /* --network */
419 if (m_strNetworkName.isNotEmpty())
420 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "multiple --network options");
421 m_strNetworkName = Val.psz;
422 break;
423
424 case 'v': /* --verbose */
425 ++uVerbosity;
426 break;
427
428
429 /*
430 * Standard options recognized by RTGetOpt()
431 */
432
433 case 'V': /* --version */
434 RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
435 return RTEXITCODE_DONE;
436
437 case 'h': /* --help */
438 return usage();
439
440 case VINF_GETOPT_NOT_OPTION:
441 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unexpected non-option argument");
442
443 default:
444 return RTGetOptPrintError(ch, &Val);
445 }
446 }
447
448 if (m_strNetworkName.isEmpty())
449 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "missing --network option");
450
451 m_uVerbosity = uVerbosity;
452 return RTEXITCODE_SUCCESS;
453}
454
455
456/**
457 * Perform actual initialization.
458 *
459 * This code runs on the main thread. Establish COM connection with
460 * VBoxSVC so that we can do API calls. Starts the LWIP thread.
461 */
462int VBoxNetSlirpNAT::init()
463{
464 HRESULT hrc;
465 int rc;
466
467 LogFlowFuncEnter();
468
469 /* Get the COM API set up. */
470 rc = initCom();
471 if (RT_FAILURE(rc))
472 return rc;
473
474 /* Get the home folder location. It's ok if it fails. */
475 initHome();
476
477 /*
478 * We get the network name on the command line. Get hold of its
479 * API object to get the rest of the configuration from.
480 */
481 hrc = virtualbox->FindNATNetworkByName(com::Bstr(m_strNetworkName).raw(),
482 m_net.asOutParam());
483 if (FAILED(hrc))
484 {
485 reportComError(virtualbox, "FindNATNetworkByName", hrc);
486 return VERR_NOT_FOUND;
487 }
488
489 /*
490 * Now that we know the network name and have ensured that it
491 * indeed exists we can create the release log file.
492 */
493 initLog();
494
495 // resolver changes are reported on vbox but are retrieved from
496 // host so stash a pointer for future lookups
497 hrc = virtualbox->COMGETTER(Host)(m_host.asOutParam());
498 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
499
500 RT_ZERO(m_ProxyOptions);
501 m_u16Mtu = 1500; // XXX: FIXME
502
503 m_ProxyOptions.version = 6;
504 m_ProxyOptions.restricted = false;
505 m_ProxyOptions.in_enabled = true;
506 m_ProxyOptions.if_mtu = m_u16Mtu;
507 m_ProxyOptions.disable_dhcp = true;
508
509 /* Get the settings related to IPv4. */
510 rc = initIPv4();
511 if (RT_FAILURE(rc))
512 return rc;
513
514 /* Get the settings related to IPv6. */
515 rc = initIPv6();
516 if (RT_FAILURE(rc))
517 return rc;
518
519
520 fetchNatPortForwardRules(m_vecPortForwardRule4, /* :fIsIPv6 */ false);
521 if (m_ProxyOptions.in6_enabled)
522 fetchNatPortForwardRules(m_vecPortForwardRule6, /* :fIsIPv6 */ true);
523
524
525 if (m_strHome.isNotEmpty())
526 {
527 com::Utf8StrFmt strTftpRoot("%s%c%s", m_strHome.c_str(), RTPATH_DELIMITER, "TFTP");
528 char *pszStrTemp; // avoid const char ** vs char **
529 rc = RTStrUtf8ToCurrentCP(&pszStrTemp, strTftpRoot.c_str());
530 AssertRC(rc);
531 m_ProxyOptions.tftp_path = pszStrTemp;
532 }
533
534#if 0 /** @todo */
535 m_ProxyOptions.nameservers = getHostNameservers();
536#endif
537
538 static SlirpCb slirpCallbacks = { 0 };
539
540 slirpCallbacks.send_packet = slirpSendPacketCb;
541 slirpCallbacks.guest_error = slirpGuestErrorCb;
542 slirpCallbacks.clock_get_ns = slirpClockGetNsCb;
543 slirpCallbacks.timer_new = slirpTimerNewCb;
544 slirpCallbacks.timer_free = slirpTimerFreeCb;
545 slirpCallbacks.timer_mod = slirpTimerModCb;
546 slirpCallbacks.notify = slirpNotifyCb;
547 slirpCallbacks.init_completed = NULL;
548 slirpCallbacks.timer_new_opaque = NULL;
549 slirpCallbacks.register_poll_socket = slirpRegisterPoll;
550 slirpCallbacks.unregister_poll_socket = slirpUnregisterPoll;
551
552 /*
553 * Initialize Slirp
554 */
555 m_pSlirp = slirp_new(/* cfg */ &m_ProxyOptions, /* callbacks */ &slirpCallbacks, /* opaque */ this);
556 if (!m_pSlirp)
557 return VERR_NO_MEMORY;
558
559 initComEvents();
560 /* end of COM initialization */
561
562 rc = RTReqQueueCreate(&m_hSlirpReqQueue);
563 AssertLogRelRCReturn(rc, rc);
564
565#ifdef RT_OS_WINDOWS
566 /* Create the wakeup socket pair (idx=0 is write, idx=1 is read). */
567 m_ahWakeupSockPair[0] = INVALID_SOCKET;
568 m_ahWakeupSockPair[1] = INVALID_SOCKET;
569 rc = RTWinSocketPair(AF_INET, SOCK_DGRAM, 0, m_ahWakeupSockPair);
570 AssertRCReturn(rc, rc);
571#else
572 /* Create the control pipe. */
573 rc = RTPipeCreate(&m_hPipeRead, &m_hPipeWrite, 0 /*fFlags*/);
574 AssertRCReturn(rc, rc);
575#endif
576
577 /* connect to the intnet */
578 rc = IntNetR3IfCreate(&m_hIf, m_strNetworkName.c_str());
579 if (RT_SUCCESS(rc))
580 rc = IntNetR3IfSetActive(m_hIf, true /*fActive*/);
581
582 LogFlowFuncLeaveRC(rc);
583 return rc;
584}
585
586
587/**
588 * Primary COM initialization performed on the main thread.
589 *
590 * This initializes COM and obtains VirtualBox Client and VirtualBox
591 * objects.
592 *
593 * @note The member variables for them are in the base class. We
594 * currently do it here so that we can report errors properly, because
595 * the base class' VBoxNetBaseService::init() is a bit naive and
596 * fixing that would just create unnecessary churn for little
597 * immediate gain. It's easier to ignore the base class code and do
598 * it ourselves and do the refactoring later.
599 */
600int VBoxNetSlirpNAT::initCom()
601{
602 HRESULT hrc;
603
604 hrc = com::Initialize();
605 if (FAILED(hrc))
606 {
607#ifdef VBOX_WITH_XPCOM
608 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
609 {
610 char szHome[RTPATH_MAX] = "";
611 int vrc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
612 if (RT_SUCCESS(vrc))
613 {
614 return RTMsgErrorExit(RTEXITCODE_INIT,
615 "Failed to initialize COM: %s: %Rhrf",
616 szHome, hrc);
617 }
618 }
619#endif /* VBOX_WITH_XPCOM */
620 return RTMsgErrorExit(RTEXITCODE_INIT,
621 "Failed to initialize COM: %Rhrf", hrc);
622 }
623
624 hrc = virtualboxClient.createInprocObject(CLSID_VirtualBoxClient);
625 if (FAILED(hrc))
626 {
627 reportError("Failed to create VirtualBox Client object: %Rhra", hrc);
628 return VERR_GENERAL_FAILURE;
629 }
630
631 hrc = virtualboxClient->COMGETTER(VirtualBox)(virtualbox.asOutParam());
632 if (FAILED(hrc))
633 {
634 reportError("Failed to obtain VirtualBox object: %Rhra", hrc);
635 return VERR_GENERAL_FAILURE;
636 }
637
638 return VINF_SUCCESS;
639}
640
641
642/**
643 * Get the VirtualBox home folder.
644 *
645 * It is used as the base directory for the default release log file
646 * and for the TFTP root location.
647 */
648int VBoxNetSlirpNAT::initHome()
649{
650 HRESULT hrc;
651 int rc;
652
653 com::Bstr bstrHome;
654 hrc = virtualbox->COMGETTER(HomeFolder)(bstrHome.asOutParam());
655 if (SUCCEEDED(hrc))
656 {
657 m_strHome = bstrHome;
658 return VINF_SUCCESS;
659 }
660
661 /*
662 * In the unlikely event that we have failed to retrieve
663 * HomeFolder via the API, try the fallback method. Note that
664 * despite "com" namespace it does not use COM.
665 */
666 char szHome[RTPATH_MAX] = "";
667 rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
668 if (RT_SUCCESS(rc))
669 {
670 m_strHome = szHome;
671 return VINF_SUCCESS;
672 }
673
674 return rc;
675}
676
677
678/*
679 * Read IPv4 related settings and do necessary initialization. These
680 * settings will be picked up by the proxy on the lwIP thread. See
681 * onLwipTcpIpInit().
682 */
683int VBoxNetSlirpNAT::initIPv4()
684{
685 HRESULT hrc;
686 int rc;
687
688 AssertReturn(m_net.isNotNull(), VERR_GENERAL_FAILURE);
689
690
691 /*
692 * IPv4 address and mask.
693 */
694 com::Bstr bstrIPv4Prefix;
695 hrc = m_net->COMGETTER(Network)(bstrIPv4Prefix.asOutParam());
696 if (FAILED(hrc))
697 {
698 reportComError(m_net, "Network", hrc);
699 return VERR_GENERAL_FAILURE;
700 }
701
702 RTNETADDRIPV4 Net4, Mask4;
703 int iPrefixLength;
704 rc = RTNetStrToIPv4Cidr(com::Utf8Str(bstrIPv4Prefix).c_str(),
705 &Net4, &iPrefixLength);
706 if (RT_FAILURE(rc))
707 {
708 reportError("Failed to parse IPv4 prefix %ls\n", bstrIPv4Prefix.raw());
709 return rc;
710 }
711
712 if (iPrefixLength > 30 || 0 >= iPrefixLength)
713 {
714 reportError("Invalid IPv4 prefix length %d\n", iPrefixLength);
715 return VERR_INVALID_PARAMETER;
716 }
717
718 rc = RTNetPrefixToMaskIPv4(iPrefixLength, &Mask4);
719 AssertRCReturn(rc, rc);
720
721 /** @todo r=uwe Check the address is unicast, not a loopback, etc. */
722
723 RTNETADDRIPV4 Addr4;
724 Addr4.u = Net4.u | RT_H2N_U32_C(0x00000001);
725
726 memcpy(&m_ProxyOptions.vnetwork, &Net4, sizeof(in_addr));
727 memcpy(&m_ProxyOptions.vnetmask, &Mask4, sizeof(in_addr));
728 memcpy(&m_ProxyOptions.vhost, &Addr4, sizeof(in_addr));
729
730#if 0 /** @todo */
731 /* Raw socket for ICMP. */
732 initIPv4RawSock();
733
734 /* IPv4 source address (host), if configured. */
735 com::Utf8Str strSourceIp4;
736 rc = getExtraData(strSourceIp4, "SourceIp4");
737 if (RT_SUCCESS(rc) && strSourceIp4.isNotEmpty())
738 {
739 RTNETADDRIPV4 addr;
740 rc = RTNetStrToIPv4Addr(strSourceIp4.c_str(), &addr);
741 if (RT_SUCCESS(rc))
742 {
743 m_src4.sin_addr.s_addr = addr.u;
744 m_ProxyOptions.src4 = &m_src4;
745
746 LogRel(("Will use %RTnaipv4 as IPv4 source address\n",
747 m_src4.sin_addr.s_addr));
748 }
749 else
750 {
751 LogRel(("Failed to parse \"%s\" IPv4 source address specification\n",
752 strSourceIp4.c_str()));
753 }
754 }
755
756 /* Make host's loopback(s) available from inside the natnet */
757 initIPv4LoopbackMap();
758#endif
759
760 return VINF_SUCCESS;
761}
762
763
764#if 0 /** @todo */
765/**
766 * Create raw IPv4 socket for sending and snooping ICMP.
767 */
768void VBoxNetSlirpNAT::initIPv4RawSock()
769{
770 SOCKET icmpsock4 = INVALID_SOCKET;
771
772#ifndef RT_OS_DARWIN
773 const int icmpstype = SOCK_RAW;
774#else
775 /* on OS X it's not privileged */
776 const int icmpstype = SOCK_DGRAM;
777#endif
778
779 icmpsock4 = socket(AF_INET, icmpstype, IPPROTO_ICMP);
780 if (icmpsock4 == INVALID_SOCKET)
781 {
782 perror("IPPROTO_ICMP");
783#ifdef VBOX_RAWSOCK_DEBUG_HELPER
784 icmpsock4 = getrawsock(AF_INET);
785#endif
786 }
787
788 if (icmpsock4 != INVALID_SOCKET)
789 {
790#ifdef ICMP_FILTER // Linux specific
791 struct icmp_filter flt = {
792 ~(uint32_t)(
793 (1U << ICMP_ECHOREPLY)
794 | (1U << ICMP_DEST_UNREACH)
795 | (1U << ICMP_TIME_EXCEEDED)
796 )
797 };
798
799 int status = setsockopt(icmpsock4, SOL_RAW, ICMP_FILTER,
800 &flt, sizeof(flt));
801 if (status < 0)
802 {
803 perror("ICMP_FILTER");
804 }
805#endif
806 }
807
808 m_ProxyOptions.icmpsock4 = icmpsock4;
809}
810
811
812/**
813 * Init mapping from the natnet's IPv4 addresses to host's IPv4
814 * loopbacks. Plural "loopbacks" because it's now quite common to run
815 * services on loopback addresses other than 127.0.0.1. E.g. a
816 * caching dns proxy on 127.0.1.1 or 127.0.0.53.
817 */
818int VBoxNetSlirpNAT::initIPv4LoopbackMap()
819{
820 HRESULT hrc;
821 int rc;
822
823 com::SafeArray<BSTR> aStrLocalMappings;
824 hrc = m_net->COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(aStrLocalMappings));
825 if (FAILED(hrc))
826 {
827 reportComError(m_net, "LocalMappings", hrc);
828 return VERR_GENERAL_FAILURE;
829 }
830
831 if (aStrLocalMappings.size() == 0)
832 return VINF_SUCCESS;
833
834
835 /* netmask in host order, to verify the offsets */
836 uint32_t uMask = RT_N2H_U32(ip4_addr_get_u32(&m_ProxyOptions.ipv4_mask.u_addr.ip4));
837
838
839 /*
840 * Process mappings of the form "127.x.y.z=off"
841 */
842 unsigned int dst = 0; /* typeof(ip4_lomap_desc::num_lomap) */
843 for (size_t i = 0; i < aStrLocalMappings.size(); ++i)
844 {
845 com::Utf8Str strMapping(aStrLocalMappings[i]);
846 const char *pcszRule = strMapping.c_str();
847 LogRel(("IPv4 loopback mapping %zu: %s\n", i, pcszRule));
848
849 RTNETADDRIPV4 Loopback4;
850 char *pszNext;
851 rc = RTNetStrToIPv4AddrEx(pcszRule, &Loopback4, &pszNext);
852 if (RT_FAILURE(rc))
853 {
854 LogRel(("Failed to parse IPv4 address: %Rra\n", rc));
855 continue;
856 }
857
858 if (Loopback4.au8[0] != 127)
859 {
860 LogRel(("Not an IPv4 loopback address\n"));
861 continue;
862 }
863
864 if (rc != VWRN_TRAILING_CHARS)
865 {
866 LogRel(("Missing right hand side\n"));
867 continue;
868 }
869
870 pcszRule = RTStrStripL(pszNext);
871 if (*pcszRule != '=')
872 {
873 LogRel(("Invalid rule format\n"));
874 continue;
875 }
876
877 pcszRule = RTStrStripL(pcszRule+1);
878 if (*pszNext == '\0')
879 {
880 LogRel(("Empty right hand side\n"));
881 continue;
882 }
883
884 uint32_t u32Offset;
885 rc = RTStrToUInt32Ex(pcszRule, &pszNext, 10, &u32Offset);
886 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
887 {
888 LogRel(("Invalid offset\n"));
889 continue;
890 }
891
892 if (u32Offset <= 1 || u32Offset == ~uMask)
893 {
894 LogRel(("Offset maps to a reserved address\n"));
895 continue;
896 }
897
898 if ((u32Offset & uMask) != 0)
899 {
900 LogRel(("Offset exceeds the network size\n"));
901 continue;
902 }
903
904 if (dst >= RT_ELEMENTS(m_lo2off))
905 {
906 LogRel(("Ignoring the mapping, too many mappings already\n"));
907 continue;
908 }
909
910 ip4_addr_set_u32(&m_lo2off[dst].loaddr, Loopback4.u);
911 m_lo2off[dst].off = u32Offset;
912 ++dst;
913 }
914
915 if (dst > 0)
916 {
917 m_loOptDescriptor.lomap = m_lo2off;
918 m_loOptDescriptor.num_lomap = dst;
919 m_ProxyOptions.lomap_desc = &m_loOptDescriptor;
920 }
921
922 return VINF_SUCCESS;
923}
924#endif
925
926
927/*
928 * Read IPv6 related settings and do necessary initialization. These
929 * settings will be picked up by the proxy on the lwIP thread. See
930 * onLwipTcpIpInit().
931 */
932int VBoxNetSlirpNAT::initIPv6()
933{
934 HRESULT hrc;
935 int rc;
936
937 AssertReturn(m_net.isNotNull(), VERR_GENERAL_FAILURE);
938
939
940 /* Is IPv6 enabled for this network at all? */
941 BOOL fIPv6Enabled = FALSE;
942 hrc = m_net->COMGETTER(IPv6Enabled)(&fIPv6Enabled);
943 if (FAILED(hrc))
944 {
945 reportComError(m_net, "IPv6Enabled", hrc);
946 return VERR_GENERAL_FAILURE;
947 }
948
949 m_ProxyOptions.in6_enabled = !!fIPv6Enabled;
950 if (!fIPv6Enabled)
951 return VINF_SUCCESS;
952
953
954 /*
955 * IPv6 address.
956 */
957 com::Bstr bstrIPv6Prefix;
958 hrc = m_net->COMGETTER(IPv6Prefix)(bstrIPv6Prefix.asOutParam());
959 if (FAILED(hrc))
960 {
961 reportComError(m_net, "IPv6Prefix", hrc);
962 return VERR_GENERAL_FAILURE;
963 }
964
965 RTNETADDRIPV6 Net6;
966 int iPrefixLength;
967 rc = RTNetStrToIPv6Cidr(com::Utf8Str(bstrIPv6Prefix).c_str(),
968 &Net6, &iPrefixLength);
969 if (RT_FAILURE(rc))
970 {
971 reportError("Failed to parse IPv6 prefix %ls\n", bstrIPv6Prefix.raw());
972 return rc;
973 }
974
975 /* Allow both addr:: and addr::/64 */
976 if (iPrefixLength == 128) /* no length was specified after the address? */
977 iPrefixLength = 64; /* take it to mean /64 which we require anyway */
978 else if (iPrefixLength != 64)
979 {
980 reportError("Invalid IPv6 prefix length %d,"
981 " must be 64.\n", iPrefixLength);
982 return rc;
983 }
984
985 /* Verify the address is unicast. */
986 if ( ((Net6.au8[0] & 0xe0) != 0x20) /* global 2000::/3 */
987 && ((Net6.au8[0] & 0xfe) != 0xfc)) /* local fc00::/7 */
988 {
989 reportError("IPv6 prefix %RTnaipv6 is not unicast.\n", &Net6);
990 return VERR_INVALID_PARAMETER;
991 }
992
993 /* Verify the interfaces ID part is zero */
994 if (Net6.au64[1] != 0)
995 {
996 reportError("Non-zero bits in the interface ID part"
997 " of the IPv6 prefix %RTnaipv6/64.\n", &Net6);
998 return VERR_INVALID_PARAMETER;
999 }
1000
1001 /* Use ...::1 as our address */
1002 RTNETADDRIPV6 Addr6 = Net6;
1003 Addr6.au8[15] = 0x01;
1004 memcpy(&m_ProxyOptions.vprefix_addr6, &Addr6, sizeof(RTNETADDRIPV6));
1005
1006
1007 /*
1008 * Should we advertise ourselves as default IPv6 route? If the
1009 * host doesn't have IPv6 connectivity, it's probably better not
1010 * to, to prevent the guest from IPv6 connection attempts doomed
1011 * to fail.
1012 *
1013 * We might want to make this modifiable while the natnet is
1014 * running.
1015 */
1016 BOOL fIPv6DefaultRoute = FALSE;
1017 hrc = m_net->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
1018 if (FAILED(hrc))
1019 {
1020 reportComError(m_net, "AdvertiseDefaultIPv6RouteEnabled", hrc);
1021 return VERR_GENERAL_FAILURE;
1022 }
1023
1024#if 0 /** @todo */
1025 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
1026
1027
1028 /* Raw socket for ICMP. */
1029 initIPv6RawSock();
1030
1031
1032 /* IPv6 source address, if configured. */
1033 com::Utf8Str strSourceIp6;
1034 rc = getExtraData(strSourceIp6, "SourceIp6");
1035 if (RT_SUCCESS(rc) && strSourceIp6.isNotEmpty())
1036 {
1037 RTNETADDRIPV6 addr;
1038 char *pszZone = NULL;
1039 rc = RTNetStrToIPv6Addr(strSourceIp6.c_str(), &addr, &pszZone);
1040 if (RT_SUCCESS(rc))
1041 {
1042 memcpy(&m_src6.sin6_addr, &addr, sizeof(addr));
1043 m_ProxyOptions.src6 = &m_src6;
1044
1045 LogRel(("Will use %RTnaipv6 as IPv6 source address\n",
1046 &m_src6.sin6_addr));
1047 }
1048 else
1049 {
1050 LogRel(("Failed to parse \"%s\" IPv6 source address specification\n",
1051 strSourceIp6.c_str()));
1052 }
1053 }
1054#endif
1055
1056 return VINF_SUCCESS;
1057}
1058
1059
1060#if 0
1061/**
1062 * Create raw IPv6 socket for sending and snooping ICMP6.
1063 */
1064void VBoxNetSlirpNAT::initIPv6RawSock()
1065{
1066 SOCKET icmpsock6 = INVALID_SOCKET;
1067
1068#ifndef RT_OS_DARWIN
1069 const int icmpstype = SOCK_RAW;
1070#else
1071 /* on OS X it's not privileged */
1072 const int icmpstype = SOCK_DGRAM;
1073#endif
1074
1075 icmpsock6 = socket(AF_INET6, icmpstype, IPPROTO_ICMPV6);
1076 if (icmpsock6 == INVALID_SOCKET)
1077 {
1078 perror("IPPROTO_ICMPV6");
1079#ifdef VBOX_RAWSOCK_DEBUG_HELPER
1080 icmpsock6 = getrawsock(AF_INET6);
1081#endif
1082 }
1083
1084 if (icmpsock6 != INVALID_SOCKET)
1085 {
1086#ifdef ICMP6_FILTER // Windows doesn't support RFC 3542 API
1087 /*
1088 * XXX: We do this here for now, not in pxping.c, to avoid
1089 * name clashes between lwIP and system headers.
1090 */
1091 struct icmp6_filter flt;
1092 ICMP6_FILTER_SETBLOCKALL(&flt);
1093
1094 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &flt);
1095
1096 ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &flt);
1097 ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &flt);
1098 ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &flt);
1099 ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &flt);
1100
1101 int status = setsockopt(icmpsock6, IPPROTO_ICMPV6, ICMP6_FILTER,
1102 &flt, sizeof(flt));
1103 if (status < 0)
1104 {
1105 perror("ICMP6_FILTER");
1106 }
1107#endif
1108 }
1109
1110 m_ProxyOptions.icmpsock6 = icmpsock6;
1111}
1112#endif
1113
1114
1115/**
1116 * Adapter for the ListenerImpl template. It has to be a separate
1117 * object because ListenerImpl deletes it. Just a small wrapper that
1118 * delegates the real work back to VBoxNetSlirpNAT.
1119 */
1120class VBoxNetSlirpNAT::Listener::Adapter
1121{
1122 VBoxNetSlirpNAT *m_pNAT;
1123public:
1124 Adapter() : m_pNAT(NULL) {}
1125 HRESULT init() { return init(NULL); }
1126 void uninit() { m_pNAT = NULL; }
1127
1128 HRESULT init(VBoxNetSlirpNAT *pNAT)
1129 {
1130 m_pNAT = pNAT;
1131 return S_OK;
1132 }
1133
1134 HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1135 {
1136 if (RT_LIKELY(m_pNAT != NULL))
1137 return m_pNAT->HandleEvent(aEventType, pEvent);
1138 else
1139 return S_OK;
1140 }
1141};
1142
1143
1144HRESULT
1145VBoxNetSlirpNAT::Listener::init(VBoxNetSlirpNAT *pNAT)
1146{
1147 HRESULT hrc;
1148
1149 hrc = m_pListenerImpl.createObject();
1150 if (FAILED(hrc))
1151 return hrc;
1152
1153 hrc = m_pListenerImpl->init(new Adapter(), pNAT);
1154 if (FAILED(hrc))
1155 {
1156 VBoxNetSlirpNAT::reportComError(m_pListenerImpl, "init", hrc);
1157 return hrc;
1158 }
1159
1160 return hrc;
1161}
1162
1163
1164void
1165VBoxNetSlirpNAT::Listener::uninit()
1166{
1167 unlisten();
1168 m_pListenerImpl.setNull();
1169}
1170
1171
1172/*
1173 * There's no base interface that exposes "eventSource" so fake it
1174 * with a template.
1175 */
1176template <typename IEventful>
1177HRESULT
1178VBoxNetSlirpNAT::Listener::listen(const ComPtr<IEventful> &pEventful,
1179 const VBoxEventType_T aEvents[])
1180{
1181 HRESULT hrc;
1182
1183 if (m_pListenerImpl.isNull())
1184 return S_OK;
1185
1186 ComPtr<IEventSource> pEventSource;
1187 hrc = pEventful->COMGETTER(EventSource)(pEventSource.asOutParam());
1188 if (FAILED(hrc))
1189 {
1190 VBoxNetSlirpNAT::reportComError(pEventful, "EventSource", hrc);
1191 return hrc;
1192 }
1193
1194 /* got a real interface, punt to the non-template code */
1195 hrc = doListen(pEventSource, aEvents);
1196 if (FAILED(hrc))
1197 return hrc;
1198
1199 return hrc;
1200}
1201
1202
1203HRESULT
1204VBoxNetSlirpNAT::Listener::doListen(const ComPtr<IEventSource> &pEventSource,
1205 const VBoxEventType_T aEvents[])
1206{
1207 HRESULT hrc;
1208
1209 com::SafeArray<VBoxEventType_T> aInteresting;
1210 for (size_t i = 0; aEvents[i] != VBoxEventType_Invalid; ++i)
1211 aInteresting.push_back(aEvents[i]);
1212
1213 BOOL fActive = true;
1214 hrc = pEventSource->RegisterListener(m_pListenerImpl,
1215 ComSafeArrayAsInParam(aInteresting),
1216 fActive);
1217 if (FAILED(hrc))
1218 {
1219 VBoxNetSlirpNAT::reportComError(m_pEventSource, "RegisterListener", hrc);
1220 return hrc;
1221 }
1222
1223 m_pEventSource = pEventSource;
1224 return hrc;
1225}
1226
1227
1228HRESULT
1229VBoxNetSlirpNAT::Listener::unlisten()
1230{
1231 HRESULT hrc;
1232
1233 if (m_pEventSource.isNull())
1234 return S_OK;
1235
1236 const ComPtr<IEventSource> pEventSource = m_pEventSource;
1237 m_pEventSource.setNull();
1238
1239 hrc = pEventSource->UnregisterListener(m_pListenerImpl);
1240 if (FAILED(hrc))
1241 {
1242 VBoxNetSlirpNAT::reportComError(pEventSource, "UnregisterListener", hrc);
1243 return hrc;
1244 }
1245
1246 return hrc;
1247}
1248
1249
1250
1251/**
1252 * Create and register API event listeners.
1253 */
1254int VBoxNetSlirpNAT::initComEvents()
1255{
1256 /**
1257 * @todo r=uwe These events are reported on both IVirtualBox and
1258 * INATNetwork objects. We used to listen for them on our
1259 * network, but it was changed later to listen on vbox. Leave it
1260 * that way for now. Note that HandleEvent() has to do additional
1261 * check for them to ignore events for other networks.
1262 */
1263 static const VBoxEventType_T s_aNATNetEvents[] = {
1264 VBoxEventType_OnNATNetworkPortForward,
1265 VBoxEventType_OnNATNetworkSetting,
1266 VBoxEventType_Invalid
1267 };
1268 m_ListenerNATNet.init(this);
1269 m_ListenerNATNet.listen(virtualbox, s_aNATNetEvents); // sic!
1270
1271 static const VBoxEventType_T s_aVirtualBoxEvents[] = {
1272 VBoxEventType_OnHostNameResolutionConfigurationChange,
1273 VBoxEventType_OnNATNetworkStartStop,
1274 VBoxEventType_Invalid
1275 };
1276 m_ListenerVirtualBox.init(this);
1277 m_ListenerVirtualBox.listen(virtualbox, s_aVirtualBoxEvents);
1278
1279 static const VBoxEventType_T s_aVBoxClientEvents[] = {
1280 VBoxEventType_OnVBoxSVCAvailabilityChanged,
1281 VBoxEventType_Invalid
1282 };
1283 m_ListenerVBoxClient.init(this);
1284 m_ListenerVBoxClient.listen(virtualboxClient, s_aVBoxClientEvents);
1285
1286 return VINF_SUCCESS;
1287}
1288
1289
1290/**
1291 * Run the pumps.
1292 *
1293 * Spawn the intnet pump thread that gets packets from the intnet and
1294 * feeds them to lwIP. Enter COM event loop here, on the main thread.
1295 */
1296int
1297VBoxNetSlirpNAT::run()
1298{
1299 AssertReturn(m_hThrRecv == NIL_RTTHREAD, VERR_INVALID_STATE);
1300 AssertReturn(m_hThrdPoll == NIL_RTTHREAD, VERR_INVALID_STATE);
1301
1302 /* Spawn the I/O polling thread. */
1303 int rc = RTThreadCreate(&m_hThrdPoll,
1304 VBoxNetSlirpNAT::pollThread, this,
1305 0, /* :cbStack */
1306 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1307 "Poll");
1308 AssertRCReturn(rc, rc);
1309
1310 /* spawn intnet input pump */
1311 rc = RTThreadCreate(&m_hThrRecv,
1312 VBoxNetSlirpNAT::receiveThread, this,
1313 0, /* :cbStack */
1314 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1315 "RECV");
1316 AssertRCReturn(rc, rc);
1317
1318 /* main thread will run the API event queue pump */
1319 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1320 if (pQueue == NULL)
1321 {
1322 LogRel(("run: getMainEventQueue() == NULL\n"));
1323 return VERR_GENERAL_FAILURE;
1324 }
1325
1326 /* dispatch API events to our listeners */
1327 for (;;)
1328 {
1329 rc = pQueue->processEventQueue(RT_INDEFINITE_WAIT);
1330 if (rc == VERR_INTERRUPTED)
1331 {
1332 LogRel(("run: shutdown\n"));
1333 break;
1334 }
1335 else if (rc != VINF_SUCCESS)
1336 {
1337 /* note any unexpected rc */
1338 LogRel(("run: processEventQueue: %Rrc\n", rc));
1339 }
1340 }
1341
1342 /*
1343 * We are out of the event loop, so we were told to shut down.
1344 * Tell other threads to wrap up.
1345 */
1346
1347 /* tell the intnet input pump to terminate */
1348 IntNetR3IfWaitAbort(m_hIf);
1349
1350 rc = RTThreadWait(m_hThrRecv, 5000, NULL);
1351 m_hThrRecv = NIL_RTTHREAD;
1352
1353 return rc;
1354}
1355
1356
1357void
1358VBoxNetSlirpNAT::shutdown()
1359{
1360 int rc;
1361
1362 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1363 if (pQueue == NULL)
1364 {
1365 LogRel(("shutdown: getMainEventQueue() == NULL\n"));
1366 return;
1367 }
1368
1369 /* unregister listeners */
1370 m_ListenerNATNet.unlisten();
1371 m_ListenerVirtualBox.unlisten();
1372 m_ListenerVBoxClient.unlisten();
1373
1374 /* tell the event loop in run() to stop */
1375 rc = pQueue->interruptEventQueueProcessing();
1376 if (RT_FAILURE(rc))
1377 LogRel(("shutdown: interruptEventQueueProcessing: %Rrc\n", rc));
1378}
1379
1380
1381/**
1382 * @note: this work on Event thread.
1383 */
1384HRESULT VBoxNetSlirpNAT::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1385{
1386 HRESULT hrc = S_OK;
1387 switch (aEventType)
1388 {
1389 case VBoxEventType_OnNATNetworkSetting:
1390 {
1391 ComPtr<INATNetworkSettingEvent> pSettingsEvent(pEvent);
1392
1393 com::Bstr networkName;
1394 hrc = pSettingsEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1395 AssertComRCReturn(hrc, hrc);
1396 if (networkName != m_strNetworkName)
1397 break; /* change not for our network */
1398
1399 // XXX: only handle IPv6 default route for now
1400 if (!m_ProxyOptions.in6_enabled)
1401 break;
1402
1403 BOOL fIPv6DefaultRoute = FALSE;
1404 hrc = pSettingsEvent->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
1405 AssertComRCReturn(hrc, hrc);
1406
1407#if 0 /** @todo */
1408 if (m_ProxyOptions.ipv6_defroute == fIPv6DefaultRoute)
1409 break;
1410
1411 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
1412 tcpip_callback_with_block(proxy_rtadvd_do_quick, &m_LwipNetIf, 0);
1413#endif
1414 break;
1415 }
1416
1417 case VBoxEventType_OnNATNetworkPortForward:
1418 {
1419 int rc = VINF_SUCCESS;
1420 ComPtr<INATNetworkPortForwardEvent> pForwardEvent = pEvent;
1421
1422 com::Bstr networkName;
1423 hrc = pForwardEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1424 AssertComRCReturn(hrc, hrc);
1425 if (networkName != m_strNetworkName)
1426 break; /* change not for our network */
1427
1428 BOOL fCreateFW;
1429 hrc = pForwardEvent->COMGETTER(Create)(&fCreateFW);
1430 AssertComRCReturn(hrc, hrc);
1431
1432 BOOL fIPv6FW;
1433 hrc = pForwardEvent->COMGETTER(Ipv6)(&fIPv6FW);
1434 AssertComRCReturn(hrc, hrc);
1435
1436 com::Bstr name;
1437 hrc = pForwardEvent->COMGETTER(Name)(name.asOutParam());
1438 AssertComRCReturn(hrc, hrc);
1439
1440 NATProtocol_T proto = NATProtocol_TCP;
1441 hrc = pForwardEvent->COMGETTER(Proto)(&proto);
1442 AssertComRCReturn(hrc, hrc);
1443
1444 com::Bstr strHostAddr;
1445 hrc = pForwardEvent->COMGETTER(HostIp)(strHostAddr.asOutParam());
1446 AssertComRCReturn(hrc, hrc);
1447
1448 LONG lHostPort;
1449 hrc = pForwardEvent->COMGETTER(HostPort)(&lHostPort);
1450 AssertComRCReturn(hrc, hrc);
1451
1452 com::Bstr strGuestAddr;
1453 hrc = pForwardEvent->COMGETTER(GuestIp)(strGuestAddr.asOutParam());
1454 AssertComRCReturn(hrc, hrc);
1455
1456 LONG lGuestPort;
1457 hrc = pForwardEvent->COMGETTER(GuestPort)(&lGuestPort);
1458 AssertComRCReturn(hrc, hrc);
1459
1460 PPORTFORWARDRULE pNatPf = (PPORTFORWARDRULE)RTMemAllocZ(sizeof(*pNatPf));
1461 if (!pNatPf)
1462 {
1463 hrc = E_OUTOFMEMORY;
1464 goto port_forward_done;
1465 }
1466
1467 pNatPf->fPfrIPv6 = fIPv6FW;
1468
1469 switch (proto)
1470 {
1471 case NATProtocol_TCP:
1472 pNatPf->iPfrProto = IPPROTO_TCP;
1473 break;
1474 case NATProtocol_UDP:
1475 pNatPf->iPfrProto = IPPROTO_UDP;
1476 break;
1477
1478 default:
1479 LogRel(("Event: %s %s port-forwarding rule \"%s\": invalid protocol %d\n",
1480 fCreateFW ? "Add" : "Remove",
1481 fIPv6FW ? "IPv6" : "IPv4",
1482 com::Utf8Str(name).c_str(),
1483 (int)proto));
1484 goto port_forward_done;
1485 }
1486
1487 LogRel(("Event: %s %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1488 fCreateFW ? "Add" : "Remove",
1489 fIPv6FW ? "IPv6" : "IPv4",
1490 com::Utf8Str(name).c_str(),
1491 proto == NATProtocol_TCP ? "TCP" : "UDP",
1492 /* from */
1493 fIPv6FW ? "[" : "",
1494 com::Utf8Str(strHostAddr).c_str(),
1495 fIPv6FW ? "]" : "",
1496 lHostPort,
1497 /* to */
1498 fIPv6FW ? "[" : "",
1499 com::Utf8Str(strGuestAddr).c_str(),
1500 fIPv6FW ? "]" : "",
1501 lGuestPort));
1502
1503 if (name.length() > sizeof(pNatPf->szPfrName))
1504 {
1505 hrc = E_INVALIDARG;
1506 goto port_forward_done;
1507 }
1508
1509 RTStrPrintf(pNatPf->szPfrName, sizeof(pNatPf->szPfrName),
1510 "%s", com::Utf8Str(name).c_str());
1511
1512 RTStrPrintf(pNatPf->szPfrHostAddr, sizeof(pNatPf->szPfrHostAddr),
1513 "%s", com::Utf8Str(strHostAddr).c_str());
1514
1515 /* XXX: limits should be checked */
1516 pNatPf->u16PfrHostPort = (uint16_t)lHostPort;
1517
1518 RTStrPrintf(pNatPf->szPfrGuestAddr, sizeof(pNatPf->szPfrGuestAddr),
1519 "%s", com::Utf8Str(strGuestAddr).c_str());
1520
1521 /* XXX: limits should be checked */
1522 pNatPf->u16PfrGuestPort = (uint16_t)lGuestPort;
1523
1524 rc = RTReqQueueCallEx(m_hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1525 (PFNRT)natServicePfRegister, 4, this, pNatPf, !fCreateFW /*fRemove*/, true /*fRuntime*/);
1526 if (RT_FAILURE(rc))
1527 RTMemFree(pNatPf);
1528
1529 port_forward_done:
1530 /* clean up strings */
1531 name.setNull();
1532 strHostAddr.setNull();
1533 strGuestAddr.setNull();
1534 break;
1535 }
1536
1537 case VBoxEventType_OnHostNameResolutionConfigurationChange:
1538 {
1539#if 0 /** @todo */
1540 const char **ppcszNameServers = getHostNameservers();
1541 err_t error;
1542
1543 error = tcpip_callback_with_block(pxdns_set_nameservers,
1544 ppcszNameServers,
1545 /* :block */ 0);
1546 if (error != ERR_OK && ppcszNameServers != NULL)
1547 RTMemFree(ppcszNameServers);
1548#endif
1549 break;
1550 }
1551
1552 case VBoxEventType_OnNATNetworkStartStop:
1553 {
1554 ComPtr <INATNetworkStartStopEvent> pStartStopEvent = pEvent;
1555
1556 com::Bstr networkName;
1557 hrc = pStartStopEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1558 AssertComRCReturn(hrc, hrc);
1559 if (networkName != m_strNetworkName)
1560 break; /* change not for our network */
1561
1562 BOOL fStart = TRUE;
1563 hrc = pStartStopEvent->COMGETTER(StartEvent)(&fStart);
1564 AssertComRCReturn(hrc, hrc);
1565
1566 if (!fStart)
1567 shutdown();
1568 break;
1569 }
1570
1571 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
1572 {
1573 LogRel(("VBoxSVC became unavailable, exiting.\n"));
1574 shutdown();
1575 break;
1576 }
1577
1578 default: break; /* Shut up MSC. */
1579 }
1580 return hrc;
1581}
1582
1583
1584/**
1585 * Read the list of host's resolvers via the API.
1586 *
1587 * Called during initialization and in response to the
1588 * VBoxEventType_OnHostNameResolutionConfigurationChange event.
1589 */
1590const char **VBoxNetSlirpNAT::getHostNameservers()
1591{
1592 if (m_host.isNull())
1593 return NULL;
1594
1595 com::SafeArray<BSTR> aNameServers;
1596 HRESULT hrc = m_host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(aNameServers));
1597 if (FAILED(hrc))
1598 return NULL;
1599
1600 const size_t cNameServers = aNameServers.size();
1601 if (cNameServers == 0)
1602 return NULL;
1603
1604 const char **ppcszNameServers =
1605 (const char **)RTMemAllocZ(sizeof(char *) * (cNameServers + 1));
1606 if (ppcszNameServers == NULL)
1607 return NULL;
1608
1609 size_t idxLast = 0;
1610 for (size_t i = 0; i < cNameServers; ++i)
1611 {
1612 com::Utf8Str strNameServer(aNameServers[i]);
1613 ppcszNameServers[idxLast] = RTStrDup(strNameServer.c_str());
1614 if (ppcszNameServers[idxLast] != NULL)
1615 ++idxLast;
1616 }
1617
1618 if (idxLast == 0)
1619 {
1620 RTMemFree(ppcszNameServers);
1621 return NULL;
1622 }
1623
1624 return ppcszNameServers;
1625}
1626
1627
1628/**
1629 * Fetch port-forwarding rules via the API.
1630 *
1631 * Reads the initial sets of rules from VBoxSVC. The rules will be
1632 * activated when all the initialization and plumbing is done. See
1633 * natServiceProcessRegisteredPf().
1634 */
1635int VBoxNetSlirpNAT::fetchNatPortForwardRules(VECNATSERVICEPF &vec, bool fIsIPv6)
1636{
1637 HRESULT hrc;
1638
1639 com::SafeArray<BSTR> rules;
1640 if (fIsIPv6)
1641 hrc = m_net->COMGETTER(PortForwardRules6)(ComSafeArrayAsOutParam(rules));
1642 else
1643 hrc = m_net->COMGETTER(PortForwardRules4)(ComSafeArrayAsOutParam(rules));
1644 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
1645
1646 NATSERVICEPORTFORWARDRULE Rule;
1647 for (size_t idxRules = 0; idxRules < rules.size(); ++idxRules)
1648 {
1649 Log(("%d-%s rule: %ls\n", idxRules, (fIsIPv6 ? "IPv6" : "IPv4"), rules[idxRules]));
1650 RT_ZERO(Rule);
1651
1652 int rc = netPfStrToPf(com::Utf8Str(rules[idxRules]).c_str(), fIsIPv6,
1653 &Rule.Pfr);
1654 if (RT_FAILURE(rc))
1655 continue;
1656
1657 vec.push_back(Rule);
1658 }
1659
1660 return VINF_SUCCESS;
1661}
1662
1663
1664/**
1665 * Activate the initial set of port-forwarding rules.
1666 */
1667int VBoxNetSlirpNAT::natServiceProcessRegisteredPf(VECNATSERVICEPF& vecRules)
1668{
1669 ITERATORNATSERVICEPF it;
1670 for (it = vecRules.begin(); it != vecRules.end(); ++it)
1671 {
1672 NATSERVICEPORTFORWARDRULE &natPf = *it;
1673
1674 LogRel(("Loading %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1675 natPf.Pfr.fPfrIPv6 ? "IPv6" : "IPv4",
1676 natPf.Pfr.szPfrName,
1677 natPf.Pfr.iPfrProto == IPPROTO_TCP ? "TCP" : "UDP",
1678 /* from */
1679 natPf.Pfr.fPfrIPv6 ? "[" : "",
1680 natPf.Pfr.szPfrHostAddr,
1681 natPf.Pfr.fPfrIPv6 ? "]" : "",
1682 natPf.Pfr.u16PfrHostPort,
1683 /* to */
1684 natPf.Pfr.fPfrIPv6 ? "[" : "",
1685 natPf.Pfr.szPfrGuestAddr,
1686 natPf.Pfr.fPfrIPv6 ? "]" : "",
1687 natPf.Pfr.u16PfrGuestPort));
1688
1689 natServicePfRegister(this, &natPf.Pfr, false /*fRemove*/, false /*fRuntime*/);
1690 }
1691
1692 return VINF_SUCCESS;
1693}
1694
1695
1696/**
1697 * Activate a single port-forwarding rule.
1698 *
1699 * This is used both when we activate all the initial rules on startup
1700 * and when port-forwarding rules are changed and we are notified via
1701 * an API event.
1702 */
1703/* static */
1704DECLCALLBACK(void) VBoxNetSlirpNAT::natServicePfRegister(VBoxNetSlirpNAT *pThis, PPORTFORWARDRULE pNatPf, bool fRemove, bool fRuntime)
1705{
1706 bool fUdp;
1707 switch(pNatPf->iPfrProto)
1708 {
1709 case IPPROTO_TCP:
1710 fUdp = false;
1711 break;
1712 case IPPROTO_UDP:
1713 fUdp = true;
1714 break;
1715 default:
1716 return;
1717 }
1718
1719 const char *pszHostAddr = pNatPf->szPfrHostAddr;
1720 if (pszHostAddr[0] == '\0')
1721 {
1722 if (pNatPf->fPfrIPv6)
1723 pszHostAddr = "::";
1724 else
1725 pszHostAddr = "0.0.0.0";
1726 }
1727
1728 const char *pszGuestAddr = pNatPf->szPfrGuestAddr;
1729 if (pszGuestAddr[0] == '\0')
1730 {
1731 if (pNatPf->fPfrIPv6)
1732 pszGuestAddr = "::";
1733 else
1734 pszGuestAddr = "0.0.0.0";
1735 }
1736
1737 struct in_addr guestIp, hostIp;
1738 if (inet_aton(pszHostAddr, &hostIp) == 0)
1739 hostIp.s_addr = INADDR_ANY;
1740
1741 if (inet_aton(pszGuestAddr, &guestIp) == 0)
1742 {
1743 LogRel(("Unable to convert guest address '%s' for %s rule \"%s\"\n",
1744 pszGuestAddr, pNatPf->fPfrIPv6 ? "IPv6" : "IPv4",
1745 pNatPf->szPfrName));
1746 return;
1747 }
1748
1749 int rc;
1750 if (fRemove)
1751 rc = slirp_remove_hostfwd(pThis->m_pSlirp, fUdp, hostIp, pNatPf->u16PfrHostPort);
1752 else
1753 rc = slirp_add_hostfwd(pThis->m_pSlirp, fUdp,
1754 hostIp, pNatPf->u16PfrHostPort,
1755 guestIp, pNatPf->u16PfrGuestPort);
1756 if (!rc)
1757 {
1758 if (fRuntime)
1759 {
1760 VECNATSERVICEPF& rules = pNatPf->fPfrIPv6 ? pThis->m_vecPortForwardRule6
1761 : pThis->m_vecPortForwardRule4;
1762 if (fRemove)
1763 {
1764 ITERATORNATSERVICEPF it;
1765 for (it = rules.begin(); it != rules.end(); ++it)
1766 {
1767 /* compare */
1768 NATSERVICEPORTFORWARDRULE &natFw = *it;
1769 if ( natFw.Pfr.iPfrProto == pNatPf->iPfrProto
1770 && natFw.Pfr.u16PfrHostPort == pNatPf->u16PfrHostPort
1771 && strncmp(natFw.Pfr.szPfrHostAddr, pNatPf->szPfrHostAddr, INET6_ADDRSTRLEN) == 0
1772 && natFw.Pfr.u16PfrGuestPort == pNatPf->u16PfrGuestPort
1773 && strncmp(natFw.Pfr.szPfrGuestAddr, pNatPf->szPfrGuestAddr, INET6_ADDRSTRLEN) == 0)
1774 {
1775 rules.erase(it);
1776 break;
1777 }
1778 } /* loop over vector elements */
1779 }
1780 else /* Addition */
1781 {
1782 NATSERVICEPORTFORWARDRULE r;
1783 RT_ZERO(r);
1784 memcpy(&r.Pfr, pNatPf, sizeof(*pNatPf));
1785 rules.push_back(r);
1786 } /* condition add or delete */
1787 }
1788 else /* The rules vector is already up to date. */
1789 Assert(fRemove == false);
1790 }
1791 else
1792 LogRel(("Unable to %s %s rule \"%s\"\n",
1793 fRemove ? "remove" : "add",
1794 pNatPf->fPfrIPv6 ? "IPv6" : "IPv4",
1795 pNatPf->szPfrName));
1796
1797 /* Free the rule in any case. */
1798 RTMemFree(pNatPf);
1799}
1800
1801
1802/**
1803 * Converts slirp representation of poll events to host representation.
1804 *
1805 * @param iEvents Integer representing slirp type poll events.
1806 *
1807 * @returns Integer representing host type poll events.
1808 */
1809DECLINLINE(short) pollEventSlirpToHost(int iEvents)
1810{
1811 short iRet = 0;
1812#ifndef RT_OS_WINDOWS
1813 if (iEvents & SLIRP_POLL_IN) iRet |= POLLIN;
1814 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLOUT;
1815 if (iEvents & SLIRP_POLL_PRI) iRet |= POLLPRI;
1816 if (iEvents & SLIRP_POLL_ERR) iRet |= POLLERR;
1817 if (iEvents & SLIRP_POLL_HUP) iRet |= POLLHUP;
1818#else
1819 if (iEvents & SLIRP_POLL_IN) iRet |= (POLLRDNORM | POLLRDBAND);
1820 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLWRNORM;
1821 if (iEvents & SLIRP_POLL_PRI) iRet |= (POLLIN);
1822 if (iEvents & SLIRP_POLL_ERR) iRet |= 0;
1823 if (iEvents & SLIRP_POLL_HUP) iRet |= 0;
1824#endif
1825 return iRet;
1826}
1827
1828/**
1829 * Converts host representation of poll events to slirp representation.
1830 *
1831 * @param iEvents Integer representing host type poll events.
1832 *
1833 * @returns Integer representing slirp type poll events.
1834 *
1835 * @thread ?
1836 */
1837DECLINLINE(int) pollEventHostToSlirp(int iEvents)
1838{
1839 int iRet = 0;
1840#ifndef RT_OS_WINDOWS
1841 if (iEvents & POLLIN) iRet |= SLIRP_POLL_IN;
1842 if (iEvents & POLLOUT) iRet |= SLIRP_POLL_OUT;
1843 if (iEvents & POLLPRI) iRet |= SLIRP_POLL_PRI;
1844 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
1845 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
1846#else
1847 if (iEvents & (POLLRDNORM | POLLRDBAND)) iRet |= SLIRP_POLL_IN;
1848 if (iEvents & POLLWRNORM) iRet |= SLIRP_POLL_OUT;
1849 if (iEvents & (POLLPRI)) iRet |= SLIRP_POLL_PRI;
1850 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
1851 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
1852#endif
1853 return iRet;
1854}
1855
1856
1857/*
1858 * Libslirp Callbacks
1859 */
1860/**
1861 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
1862 */
1863void VBoxNetSlirpNAT::slirpNotifyPollThread(const char *pszWho)
1864{
1865 RT_NOREF(pszWho);
1866#ifdef RT_OS_WINDOWS
1867 int cbWritten = send(m_ahWakeupSockPair[0], "", 1, NULL);
1868 if (RT_LIKELY(cbWritten != SOCKET_ERROR))
1869 ASMAtomicIncU64(&m_cWakeupNotifs);
1870 else
1871 Log4(("Notify NAT Thread Error %d\n", WSAGetLastError()));
1872#else
1873 /* kick poll() */
1874 size_t cbIgnored;
1875 int rc = RTPipeWrite(m_hPipeWrite, "", 1, &cbIgnored);
1876 AssertRC(rc);
1877 if (RT_SUCCESS(rc))
1878 ASMAtomicIncU64(&m_cWakeupNotifs);
1879#endif
1880}
1881
1882
1883/**
1884 * Callback called by libslirp to send packet into the internal network.
1885 *
1886 * @param pvBuf Pointer to packet buffer.
1887 * @param cb Size of packet.
1888 * @param pvUser Pointer to NAT State context.
1889 *
1890 * @returns Size of packet received or -1 on error.
1891 *
1892 * @thread ?
1893 */
1894/*static*/ ssize_t VBoxNetSlirpNAT::slirpSendPacketCb(const void *pvBuf, ssize_t cb, void *pvUser) RT_NOTHROW_DEF
1895{
1896 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
1897 AssertPtrReturn(pThis, -1);
1898
1899 INTNETFRAME Frame;
1900 int rc = IntNetR3IfQueryOutputFrame(pThis->m_hIf, (uint32_t)cb, &Frame);
1901 if (RT_FAILURE(rc))
1902 return -1;
1903
1904 memcpy(Frame.pvFrame, pvBuf, cb);
1905 rc = IntNetR3IfOutputFrameCommit(pThis->m_hIf, &Frame);
1906 if (RT_FAILURE(rc))
1907 return -1;
1908 return cb;
1909}
1910
1911
1912/**
1913 * Callback called by libslirp when the guest does something wrong.
1914 *
1915 * @param pszMsg Error message string.
1916 * @param pvUser Pointer to NAT State context.
1917 *
1918 * @thread ?
1919 */
1920/*static*/ void VBoxNetSlirpNAT::slirpGuestErrorCb(const char *pszMsg, void *pvUser) RT_NOTHROW_DEF
1921{
1922 /* Note! This is _just_ libslirp complaining about odd guest behaviour. */
1923 LogRelMax(250, ("NAT Guest Error: %s\n", pszMsg));
1924 RT_NOREF(pvUser);
1925}
1926
1927
1928/**
1929 * Callback called by libslirp to get the current timestamp in nanoseconds.
1930 *
1931 * @param pvUser Pointer to NAT State context.
1932 *
1933 * @returns 64-bit signed integer representing time in nanoseconds.
1934 */
1935/*static*/ int64_t VBoxNetSlirpNAT::slirpClockGetNsCb(void *pvUser) RT_NOTHROW_DEF
1936{
1937 RT_NOREF(pvUser);
1938 return (int64_t)RTTimeNanoTS();
1939}
1940
1941/**
1942 * Callback called by slirp to create a new timer and insert it into the given list.
1943 *
1944 * @param slirpTimeCb Callback function supplied to the new timer upon timer expiry.
1945 * Called later by the timeout handler.
1946 * @param cb_opaque Opaque object supplied to slirpTimeCb when called. Should be
1947 * Identical to the opaque parameter.
1948 * @param opaque Pointer to NAT State context.
1949 *
1950 * @returns Pointer to new timer.
1951 */
1952/*static*/ void *VBoxNetSlirpNAT::slirpTimerNewCb(SlirpTimerCb slirpTimeCb, void *cb_opaque, void *opaque) RT_NOTHROW_DEF
1953{
1954 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
1955 Assert(pThis);
1956
1957 SlirpTimer * const pNewTimer = (SlirpTimer *)RTMemAlloc(sizeof(*pNewTimer));
1958 if (pNewTimer)
1959 {
1960 pNewTimer->msExpire = 0;
1961 pNewTimer->pHandler = slirpTimeCb;
1962 pNewTimer->opaque = cb_opaque;
1963 /** @todo r=bird: Not thread safe. Assumes pSlirpThread */
1964 pNewTimer->next = pThis->pTimerHead;
1965 pThis->pTimerHead = pNewTimer;
1966 }
1967 return pNewTimer;
1968}
1969
1970/**
1971 * Callback called by slirp to free a timer.
1972 *
1973 * @param pvTimer Pointer to slirpTimer object to be freed.
1974 * @param pvUser Pointer to NAT State context.
1975 */
1976/*static*/ void VBoxNetSlirpNAT::slirpTimerFreeCb(void *pvTimer, void *pvUser) RT_NOTHROW_DEF
1977{
1978 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
1979 SlirpTimer * const pTimer = (SlirpTimer *)pvTimer;
1980 Assert(pThis);
1981
1982 SlirpTimer *pPrev = NULL;
1983 SlirpTimer *pCurrent = pThis->pTimerHead;
1984 while (pCurrent != NULL)
1985 {
1986 if (pCurrent == pTimer)
1987 {
1988 /* unlink it. */
1989 if (!pPrev)
1990 pThis->pTimerHead = pCurrent->next;
1991 else
1992 pPrev->next = pCurrent->next;
1993 pCurrent->next = NULL;
1994 RTMemFree(pCurrent);
1995 return;
1996 }
1997
1998 /* advance */
1999 pPrev = pCurrent;
2000 pCurrent = pCurrent->next;
2001 }
2002 Assert(!pTimer);
2003}
2004
2005/**
2006 * Callback called by slirp to modify a timer.
2007 *
2008 * @param pvTimer Pointer to slirpTimer object to be modified.
2009 * @param msNewDeadlineTs The new absolute expiration time in milliseconds.
2010 * Zero stops it.
2011 * @param pvUser Pointer to NAT State context.
2012 */
2013/*static*/ void VBoxNetSlirpNAT::slirpTimerModCb(void *pvTimer, int64_t msNewDeadlineTs, void *pvUser) RT_NOTHROW_DEF
2014{
2015 SlirpTimer * const pTimer = (SlirpTimer *)pvTimer;
2016 pTimer->msExpire = msNewDeadlineTs;
2017 RT_NOREF(pvUser);
2018}
2019
2020/**
2021 * Callback called by slirp when there is I/O that needs to happen.
2022 *
2023 * @param opaque Pointer to NAT State context.
2024 */
2025/*static*/ void VBoxNetSlirpNAT::slirpNotifyCb(void *opaque) RT_NOTHROW_DEF
2026{
2027 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2028 RT_NOREF(pThis);
2029 //drvNATNotifyNATThread(pThis, "drvNAT_NotifyCb");
2030}
2031
2032/**
2033 * Registers poll. Unused function (other than logging).
2034 */
2035/*static*/ void VBoxNetSlirpNAT::slirpRegisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_DEF
2036{
2037 RT_NOREF(socket, opaque);
2038#ifdef RT_OS_WINDOWS
2039 Log4(("Poll registered: fd=%p\n", socket));
2040#else
2041 Log4(("Poll registered: fd=%d\n", socket));
2042#endif
2043}
2044
2045/**
2046 * Unregisters poll. Unused function (other than logging).
2047 */
2048/*static*/ void VBoxNetSlirpNAT::slirpUnregisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_DEF
2049{
2050 RT_NOREF(socket, opaque);
2051#ifdef RT_OS_WINDOWS
2052 Log4(("Poll unregistered: fd=%p\n", socket));
2053#else
2054 Log4(("Poll unregistered: fd=%d\n", socket));
2055#endif
2056}
2057
2058/**
2059 * Callback function to add entry to pollfd array.
2060 *
2061 * @param hFd Socket handle.
2062 * @param iEvents Integer of slirp type poll events.
2063 * @param opaque Pointer to NAT State context.
2064 *
2065 * @returns Index of latest pollfd entry.
2066 *
2067 * @thread ?
2068 */
2069/*static*/ int VBoxNetSlirpNAT::slirpAddPollCb(slirp_os_socket hFd, int iEvents, void *opaque) RT_NOTHROW_DEF
2070{
2071 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2072
2073 if (pThis->nsock + 1 >= pThis->uPollCap)
2074 {
2075 size_t cbNew = pThis->uPollCap * 2 * sizeof(struct pollfd);
2076 struct pollfd *pvNew = (struct pollfd *)RTMemRealloc(pThis->polls, cbNew);
2077 if (pvNew)
2078 {
2079 pThis->polls = pvNew;
2080 pThis->uPollCap *= 2;
2081 }
2082 else
2083 return -1;
2084 }
2085
2086 unsigned int uIdx = pThis->nsock;
2087 Assert(uIdx < INT_MAX);
2088 pThis->polls[uIdx].fd = hFd;
2089 pThis->polls[uIdx].events = pollEventSlirpToHost(iEvents);
2090 pThis->polls[uIdx].revents = 0;
2091 pThis->nsock += 1;
2092 return uIdx;
2093}
2094
2095/**
2096 * Get translated revents from a poll at a given index.
2097 *
2098 * @param idx Integer index of poll.
2099 * @param opaque Pointer to NAT State context.
2100 *
2101 * @returns Integer representing transalted revents.
2102 *
2103 * @thread ?
2104 */
2105/*static*/ int VBoxNetSlirpNAT::slirpGetREventsCb(int idx, void *opaque) RT_NOTHROW_DEF
2106{
2107 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2108 struct pollfd* polls = pThis->polls;
2109 return pollEventHostToSlirp(polls[idx].revents);
2110}
2111
2112
2113/**
2114 * Run expired timers.
2115 *
2116 * @thread pSlirpThread
2117 */
2118void VBoxNetSlirpNAT::timersRunExpired(void)
2119{
2120 int64_t const msNow = slirpClockGetNsCb(this) / RT_NS_1MS;
2121 SlirpTimer *pCurrent = pTimerHead;
2122 while (pCurrent != NULL)
2123 {
2124 SlirpTimer * const pNext = pCurrent->next; /* (in case the timer is destroyed from the callback) */
2125 if (pCurrent->msExpire <= msNow && pCurrent->msExpire > 0)
2126 {
2127 pCurrent->msExpire = 0;
2128 pCurrent->pHandler(pCurrent->opaque);
2129 }
2130 pCurrent = pNext;
2131 }
2132}
2133
2134
2135/**
2136 * Reduce the given timeout to match the earliest timer deadline.
2137 *
2138 * @returns Updated cMsTimeout value.
2139 * @param cMsTimeout The timeout to adjust, in milliseconds.
2140 *
2141 * @thread pSlirpThread
2142 */
2143int VBoxNetSlirpNAT::slirpTimersAdjustTimeoutDown(int cMsTimeout)
2144{
2145 /** @todo r=bird: This and a most other stuff would be easier if msExpire was
2146 * unsigned and we used UINT64_MAX for stopped timers. */
2147 /** @todo The timer code isn't thread safe, it assumes a single user thread
2148 * (pSlirpThread). */
2149
2150 /* Find the first (lowest) deadline. */
2151 int64_t msDeadline = INT64_MAX;
2152 for (SlirpTimer *pCurrent = pTimerHead; pCurrent; pCurrent = pCurrent->next)
2153 if (pCurrent->msExpire < msDeadline && pCurrent->msExpire > 0)
2154 msDeadline = pCurrent->msExpire;
2155
2156 /* Adjust the timeout if there is a timer with a deadline. */
2157 if (msDeadline < INT64_MAX)
2158 {
2159 int64_t const msNow = slirpClockGetNsCb(NULL) / RT_NS_1MS;
2160 if (msNow < msDeadline)
2161 {
2162 int64_t cMilliesToDeadline = msDeadline - msNow;
2163 if (cMilliesToDeadline < cMsTimeout)
2164 cMsTimeout = (int)cMilliesToDeadline;
2165 }
2166 else
2167 cMsTimeout = 0;
2168 }
2169
2170 return cMsTimeout;
2171}
2172
2173
2174/**
2175 * Slirp polling thread.
2176 */
2177/* static */ DECLCALLBACK(int)
2178VBoxNetSlirpNAT::pollThread(RTTHREAD hThreadSelf, void *pvUser)
2179{
2180 RT_NOREF(hThreadSelf);
2181
2182 AssertReturn(pvUser != NULL, VERR_INVALID_PARAMETER);
2183 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
2184
2185 /* Activate the initial port forwarding rules. */
2186 pThis->natServiceProcessRegisteredPf(pThis->m_vecPortForwardRule4);
2187 pThis->natServiceProcessRegisteredPf(pThis->m_vecPortForwardRule6);
2188
2189 /* The first polling entry is for the control/wakeup pipe. */
2190#ifdef RT_OS_WINDOWS
2191 pThis->polls[0].fd = pThis->m_ahWakeupSockPair[1];
2192#else
2193 unsigned int cPollNegRet = 0;
2194 RTHCINTPTR const i64NativeReadPipe = RTPipeToNative(pThis->m_hPipeRead);
2195 int const fdNativeReadPipe = (int)i64NativeReadPipe;
2196 Assert(fdNativeReadPipe == i64NativeReadPipe); Assert(fdNativeReadPipe >= 0);
2197 pThis->polls[0].fd = fdNativeReadPipe;
2198 pThis->polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
2199 pThis->polls[0].revents = 0;
2200#endif /* !RT_OS_WINDOWS */
2201
2202 /*
2203 * Polling loop.
2204 */
2205 for (;;)
2206 {
2207 /*
2208 * To prevent concurrent execution of sending/receiving threads
2209 */
2210 pThis->nsock = 1;
2211
2212 uint32_t cMsTimeout = DRVNAT_DEFAULT_TIMEOUT;
2213 slirp_pollfds_fill_socket(pThis->m_pSlirp, &cMsTimeout, pThis->slirpAddPollCb /* SlirpAddPollCb */, pThis /* opaque */);
2214 cMsTimeout = pThis->slirpTimersAdjustTimeoutDown(cMsTimeout);
2215
2216#ifdef RT_OS_WINDOWS
2217 int cChangedFDs = WSAPoll(pThis->polls, pThis->nsock, cMsTimeout);
2218#else
2219 int cChangedFDs = poll(pThis->polls, pThis->nsock, cMsTimeout);
2220#endif
2221 if (cChangedFDs < 0)
2222 {
2223#ifdef RT_OS_WINDOWS
2224 int const iLastErr = WSAGetLastError(); /* (In debug builds LogRel translates to two RTLogLoggerExWeak calls.) */
2225 LogRel(("NAT: RTWinPoll returned error=%Rrc (cChangedFDs=%d)\n", iLastErr, cChangedFDs));
2226 Log4(("NAT: NSOCK = %d\n", pThis->nsock));
2227#else
2228 if (errno == EINTR)
2229 {
2230 Log2(("NAT: signal was caught while sleep on poll\n"));
2231 /* No error, just process all outstanding requests but don't wait */
2232 cChangedFDs = 0;
2233 }
2234 else if (cPollNegRet++ > 128)
2235 {
2236 LogRel(("NAT: Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
2237 cPollNegRet = 0;
2238 }
2239#endif
2240 }
2241
2242 Log4(("%s: poll\n", __FUNCTION__));
2243 slirp_pollfds_poll(pThis->m_pSlirp, cChangedFDs < 0, pThis->slirpGetREventsCb, pThis /* opaque */);
2244
2245 /*
2246 * Drain the control pipe if necessary.
2247 */
2248 if (pThis->polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND)) /* POLLPRI won't be seen with WSAPoll. */
2249 {
2250 char achBuf[1024];
2251 size_t cbRead;
2252 uint64_t cWakeupNotifs = ASMAtomicReadU64(&pThis->m_cWakeupNotifs);
2253#ifdef RT_OS_WINDOWS
2254 /** @todo r=bird: This returns -1 (SOCKET_ERROR) on failure, so any kind of
2255 * error return here and we'll bugger up cbWakeupNotifs! */
2256 cbRead = recv(pThis->m_ahWakeupSockPair[1], &achBuf[0], RT_MIN(cWakeupNotifs, sizeof(achBuf)), NULL);
2257#else
2258 /** @todo r=bird: cbRead may in theory be used uninitialized here! This
2259 * isn't blocking, though, so we won't get stuck here if we mess up
2260 * the count. */
2261 RTPipeRead(pThis->m_hPipeRead, &achBuf[0], RT_MIN(cWakeupNotifs, sizeof(achBuf)), &cbRead);
2262#endif
2263 ASMAtomicSubU64(&pThis->m_cWakeupNotifs, cbRead);
2264 }
2265
2266 /* process _all_ outstanding requests but don't wait */
2267 RTReqQueueProcess(pThis->m_hSlirpReqQueue, 0);
2268 pThis->timersRunExpired();
2269 }
2270
2271#if 0
2272 LogRel(("pollThread: Exiting\n"));
2273 return VERR_INVALID_STATE;
2274#endif
2275}
2276
2277
2278/**
2279 * IntNetIf receive thread. Runs intnet pump with our processFrame()
2280 * as input callback.
2281 */
2282/* static */ DECLCALLBACK(int)
2283VBoxNetSlirpNAT::receiveThread(RTTHREAD hThreadSelf, void *pvUser)
2284{
2285 HRESULT hrc;
2286 int rc;
2287
2288 RT_NOREF(hThreadSelf);
2289
2290 AssertReturn(pvUser != NULL, VERR_INVALID_PARAMETER);
2291 VBoxNetSlirpNAT *self = static_cast<VBoxNetSlirpNAT *>(pvUser);
2292
2293 /* do we relaly need to init com on this thread? */
2294 hrc = com::Initialize();
2295 if (FAILED(hrc))
2296 return VERR_GENERAL_FAILURE;
2297
2298 rc = IntNetR3IfPumpPkts(self->m_hIf, VBoxNetSlirpNAT::processFrame, self,
2299 NULL /*pfnInputGso*/, NULL /*pvUserGso*/);
2300 if (rc == VERR_SEM_DESTROYED)
2301 return VINF_SUCCESS;
2302
2303 LogRel(("receiveThread: IntNetR3IfPumpPkts: unexpected %Rrc\n", rc));
2304 return VERR_INVALID_STATE;
2305}
2306
2307
2308/**
2309 * Worker function for drvNATSend().
2310 *
2311 * @param pThis Pointer to the NAT instance.
2312 * @param pvFrame Pointer to the frame data.
2313 * @param cbFrame Size of the frame in bytes.
2314 * @thread NAT
2315 */
2316/*static*/ DECLCALLBACK(void) VBoxNetSlirpNAT::slirpSendWorker(VBoxNetSlirpNAT *pThis, void *pvFrame, size_t cbFrame)
2317{
2318 LogFlowFunc(("pThis=%p pvFrame=%p cbFrame=%zu\n", pThis, pvFrame, cbFrame));
2319
2320 slirp_input(pThis->m_pSlirp, (uint8_t const *)pvFrame, (int)cbFrame);
2321
2322 LogFlowFunc(("leave\n"));
2323 RTMemFree(pvFrame);
2324}
2325
2326
2327/**
2328 * Process an incoming frame received from the intnet.
2329 */
2330/* static */ DECLCALLBACK(void)
2331VBoxNetSlirpNAT::processFrame(void *pvUser, void *pvFrame, uint32_t cbFrame)
2332{
2333 AssertReturnVoid(pvFrame != NULL);
2334
2335 LogFlowFunc(("processFrame:\n"));
2336
2337 /* shouldn't happen, but if it does, don't even bother */
2338 if (RT_UNLIKELY(cbFrame < sizeof(RTNETETHERHDR)))
2339 return;
2340
2341 /* we expect normal ethernet frame including .1Q and FCS */
2342 if (cbFrame > 1522)
2343 return;
2344
2345 AssertReturnVoid(pvUser != NULL);
2346 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
2347
2348 const void *pvBuf = RTMemDup(pvFrame, cbFrame);
2349 if (!pvBuf)
2350 return;
2351
2352 int rc = RTReqQueueCallEx(pThis->m_hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
2353 (PFNRT)pThis->slirpSendWorker, 3, pThis, pvBuf, cbFrame);
2354 if (RT_SUCCESS(rc))
2355 {
2356 pThis->slirpNotifyPollThread("processFrame");
2357 LogFlowFunc(("leave success\n"));
2358 }
2359}
2360
2361
2362/**
2363 * Retrieve network-specific extra data item.
2364 */
2365int VBoxNetSlirpNAT::getExtraData(com::Utf8Str &strValueOut, const char *pcszKey)
2366{
2367 HRESULT hrc;
2368
2369 AssertReturn(!virtualbox.isNull(), E_FAIL);
2370 AssertReturn(m_strNetworkName.isNotEmpty(), E_FAIL);
2371 AssertReturn(pcszKey != NULL, E_FAIL);
2372 AssertReturn(*pcszKey != '\0', E_FAIL);
2373
2374 com::BstrFmt bstrKey("NAT/%s/%s", m_strNetworkName.c_str(), pcszKey);
2375 com::Bstr bstrValue;
2376 hrc = virtualbox->GetExtraData(bstrKey.raw(), bstrValue.asOutParam());
2377 if (FAILED(hrc))
2378 {
2379 reportComError(virtualbox, "GetExtraData", hrc);
2380 return VERR_GENERAL_FAILURE;
2381 }
2382
2383 strValueOut = bstrValue;
2384 return VINF_SUCCESS;
2385}
2386
2387
2388/* static */
2389HRESULT VBoxNetSlirpNAT::reportComError(ComPtr<IUnknown> iface,
2390 const com::Utf8Str &strContext,
2391 HRESULT hrc)
2392{
2393 const com::ErrorInfo info(iface, COM_IIDOF(IUnknown));
2394 if (info.isFullAvailable() || info.isBasicAvailable())
2395 {
2396 reportErrorInfoList(info, strContext);
2397 }
2398 else
2399 {
2400 if (strContext.isNotEmpty())
2401 reportError("%s: %Rhra", strContext.c_str(), hrc);
2402 else
2403 reportError("%Rhra", hrc);
2404 }
2405
2406 return hrc;
2407}
2408
2409
2410/* static */
2411void VBoxNetSlirpNAT::reportErrorInfoList(const com::ErrorInfo &info,
2412 const com::Utf8Str &strContext)
2413{
2414 if (strContext.isNotEmpty())
2415 reportError("%s", strContext.c_str());
2416
2417 bool fFirst = true;
2418 for (const com::ErrorInfo *pInfo = &info;
2419 pInfo != NULL;
2420 pInfo = pInfo->getNext())
2421 {
2422 if (fFirst)
2423 fFirst = false;
2424 else
2425 reportError("--------");
2426
2427 reportErrorInfo(*pInfo);
2428 }
2429}
2430
2431
2432/* static */
2433void VBoxNetSlirpNAT::reportErrorInfo(const com::ErrorInfo &info)
2434{
2435#if defined (RT_OS_WIN)
2436 bool haveResultCode = info.isFullAvailable();
2437 bool haveComponent = true;
2438 bool haveInterfaceID = true;
2439#else /* !RT_OS_WIN */
2440 bool haveResultCode = true;
2441 bool haveComponent = info.isFullAvailable();
2442 bool haveInterfaceID = info.isFullAvailable();
2443#endif
2444 com::Utf8Str message;
2445 if (info.getText().isNotEmpty())
2446 message = info.getText();
2447
2448 const char *pcszDetails = "Details: ";
2449 const char *pcszComma = ", ";
2450 const char *pcszSeparator = pcszDetails;
2451
2452 if (haveResultCode)
2453 {
2454 message.appendPrintf("%s" "code %Rhrc (0x%RX32)",
2455 pcszSeparator, info.getResultCode(), info.getResultCode());
2456 pcszSeparator = pcszComma;
2457 }
2458
2459 if (haveComponent)
2460 {
2461 message.appendPrintf("%s" "component %ls",
2462 pcszSeparator, info.getComponent().raw());
2463 pcszSeparator = pcszComma;
2464 }
2465
2466 if (haveInterfaceID)
2467 {
2468 message.appendPrintf("%s" "interface %ls",
2469 pcszSeparator, info.getInterfaceName().raw());
2470 pcszSeparator = pcszComma;
2471 }
2472
2473 if (info.getCalleeName().isNotEmpty())
2474 {
2475 message.appendPrintf("%s" "callee %ls",
2476 pcszSeparator, info.getCalleeName().raw());
2477 //pcszSeparator = pcszComma; unused
2478 }
2479
2480 reportError("%s", message.c_str());
2481}
2482
2483
2484/* static */
2485void VBoxNetSlirpNAT::reportError(const char *a_pcszFormat, ...)
2486{
2487 va_list ap;
2488
2489 va_start(ap, a_pcszFormat);
2490 com::Utf8Str message(a_pcszFormat, ap);
2491 va_end(ap);
2492
2493 RTMsgError("%s", message.c_str());
2494 LogRel(("%s", message.c_str()));
2495}
2496
2497
2498
2499/**
2500 * Create release logger.
2501 *
2502 * The NAT network name is sanitized so that it can be used in a path
2503 * component. By default the release log is written to the file
2504 * ~/.VirtualBox/${netname}.log but its destiation and content can be
2505 * overridden with VBOXNET_${netname}_RELEASE_LOG family of
2506 * environment variables (also ..._DEST and ..._FLAGS).
2507 */
2508/* static */
2509int VBoxNetSlirpNAT::initLog()
2510{
2511 size_t cch;
2512 int rc;
2513
2514 if (m_strNetworkName.isEmpty())
2515 return VERR_MISSING;
2516
2517 char szNetwork[RTPATH_MAX];
2518 rc = RTStrCopy(szNetwork, sizeof(szNetwork), m_strNetworkName.c_str());
2519 if (RT_FAILURE(rc))
2520 return rc;
2521
2522 // sanitize network name to be usable as a path component
2523 for (char *p = szNetwork; *p != '\0'; ++p)
2524 {
2525 if (RTPATH_IS_SEP(*p))
2526 *p = '_';
2527 }
2528
2529 const char *pcszLogFile = NULL;
2530 char szLogFile[RTPATH_MAX];
2531 if (m_strHome.isNotEmpty())
2532 {
2533 cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
2534 "%s%c%s.log", m_strHome.c_str(), RTPATH_DELIMITER, szNetwork);
2535 if (cch < sizeof(szLogFile))
2536 pcszLogFile = szLogFile;
2537 }
2538
2539 // sanitize network name some more to be usable as environment variable
2540 for (char *p = szNetwork; *p != '\0'; ++p)
2541 {
2542 if (*p != '_'
2543 && (*p < '0' || '9' < *p)
2544 && (*p < 'a' || 'z' < *p)
2545 && (*p < 'A' || 'Z' < *p))
2546 {
2547 *p = '_';
2548 }
2549 }
2550
2551 char szEnvVarBase[128];
2552 const char *pcszEnvVarBase = szEnvVarBase;
2553 cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
2554 "VBOXNET_%s_RELEASE_LOG", szNetwork);
2555 if (cch >= sizeof(szEnvVarBase))
2556 pcszEnvVarBase = NULL;
2557
2558 rc = com::VBoxLogRelCreate("NAT Network",
2559 pcszLogFile,
2560 RTLOGFLAGS_PREFIX_TIME_PROG,
2561 "all all.restrict -default.restrict",
2562 pcszEnvVarBase,
2563 RTLOGDEST_FILE,
2564 32768 /* cMaxEntriesPerGroup */,
2565 0 /* cHistory */,
2566 0 /* uHistoryFileTime */,
2567 0 /* uHistoryFileSize */,
2568 NULL /*pErrInfo*/);
2569
2570 /*
2571 * Provide immediate feedback if corresponding LogRel level is
2572 * enabled. It's frustrating when you chase some rare event and
2573 * discover you didn't actually have the corresponding log level
2574 * enabled because of a typo in the environment variable name or
2575 * its content.
2576 */
2577#define LOG_PING(_log) _log((#_log " enabled\n"))
2578 LOG_PING(LogRel2);
2579 LOG_PING(LogRel3);
2580 LOG_PING(LogRel4);
2581 LOG_PING(LogRel5);
2582 LOG_PING(LogRel6);
2583 LOG_PING(LogRel7);
2584 LOG_PING(LogRel8);
2585 LOG_PING(LogRel9);
2586 LOG_PING(LogRel10);
2587 LOG_PING(LogRel11);
2588 LOG_PING(LogRel12);
2589
2590 return rc;
2591}
2592
2593
2594/**
2595 * Entry point.
2596 */
2597extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
2598{
2599 LogFlowFuncEnter();
2600 NOREF(envp);
2601
2602#ifdef RT_OS_WINDOWS
2603 WSADATA WsaData = {0};
2604 int err = WSAStartup(MAKEWORD(2,2), &WsaData);
2605 if (err)
2606 {
2607 fprintf(stderr, "wsastartup: failed (%d)\n", err);
2608 return RTEXITCODE_INIT;
2609 }
2610#endif
2611
2612 VBoxNetSlirpNAT NAT;
2613
2614 int rcExit = NAT.parseArgs(argc, argv);
2615 if (rcExit != RTEXITCODE_SUCCESS)
2616 {
2617 /* messages are already printed */
2618 return rcExit == RTEXITCODE_DONE ? RTEXITCODE_SUCCESS : rcExit;
2619 }
2620
2621 int rc = NAT.init();
2622 if (RT_FAILURE(rc))
2623 return RTEXITCODE_INIT;
2624
2625 NAT.run();
2626
2627 LogRel(("Terminating\n"));
2628 return RTEXITCODE_SUCCESS;
2629}
2630
2631
2632#ifndef VBOX_WITH_HARDENING
2633
2634int main(int argc, char **argv, char **envp)
2635{
2636 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
2637 if (RT_SUCCESS(rc))
2638 return TrustedMain(argc, argv, envp);
2639 return RTMsgInitFailure(rc);
2640}
2641
2642# if defined(RT_OS_WINDOWS)
2643
2644/** (We don't want a console usually.) */
2645int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2646{
2647 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
2648 return main(__argc, __argv, environ);
2649}
2650# endif /* RT_OS_WINDOWS */
2651
2652#endif /* !VBOX_WITH_HARDENING */
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