VirtualBox

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

Last change on this file was 109143, checked in by vboxsync, 6 days ago

NetworkServices/NAT/VBoxNetSlirpNAT.cpp: Some IPv6 fixes, bugref:10268

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.5 KB
Line 
1/* $Id: VBoxNetSlirpNAT.cpp 109143 2025-05-05 08:07:53Z 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 m_ProxyOptions.vprefix_len = iPrefixLength;
1002 memcpy(&m_ProxyOptions.vprefix_addr6, &Net6, sizeof(RTNETADDRIPV6));
1003
1004 /* Use ...::1 as our address */
1005 RTNETADDRIPV6 Addr6 = Net6;
1006 Addr6.au8[15] = 0x01;
1007 memcpy(&m_ProxyOptions.vhost6, &Addr6, sizeof(RTNETADDRIPV6));
1008
1009#if 0
1010 /** @todo Verify DNS server default. */
1011 Addr6.au8[15] = 0x02;
1012 memcpy(&m_ProxyOptions.vnameserver6, &Addr6, sizeof(RTNETADDRIPV6));
1013#endif
1014
1015 /*
1016 * Should we advertise ourselves as default IPv6 route? If the
1017 * host doesn't have IPv6 connectivity, it's probably better not
1018 * to, to prevent the guest from IPv6 connection attempts doomed
1019 * to fail.
1020 *
1021 * We might want to make this modifiable while the natnet is
1022 * running.
1023 */
1024 BOOL fIPv6DefaultRoute = FALSE;
1025 hrc = m_net->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
1026 if (FAILED(hrc))
1027 {
1028 reportComError(m_net, "AdvertiseDefaultIPv6RouteEnabled", hrc);
1029 return VERR_GENERAL_FAILURE;
1030 }
1031
1032#if 0 /** @todo */
1033 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
1034
1035
1036 /* Raw socket for ICMP. */
1037 initIPv6RawSock();
1038
1039
1040 /* IPv6 source address, if configured. */
1041 com::Utf8Str strSourceIp6;
1042 rc = getExtraData(strSourceIp6, "SourceIp6");
1043 if (RT_SUCCESS(rc) && strSourceIp6.isNotEmpty())
1044 {
1045 RTNETADDRIPV6 addr;
1046 char *pszZone = NULL;
1047 rc = RTNetStrToIPv6Addr(strSourceIp6.c_str(), &addr, &pszZone);
1048 if (RT_SUCCESS(rc))
1049 {
1050 memcpy(&m_src6.sin6_addr, &addr, sizeof(addr));
1051 m_ProxyOptions.src6 = &m_src6;
1052
1053 LogRel(("Will use %RTnaipv6 as IPv6 source address\n",
1054 &m_src6.sin6_addr));
1055 }
1056 else
1057 {
1058 LogRel(("Failed to parse \"%s\" IPv6 source address specification\n",
1059 strSourceIp6.c_str()));
1060 }
1061 }
1062#endif
1063
1064 return VINF_SUCCESS;
1065}
1066
1067
1068#if 0
1069/**
1070 * Create raw IPv6 socket for sending and snooping ICMP6.
1071 */
1072void VBoxNetSlirpNAT::initIPv6RawSock()
1073{
1074 SOCKET icmpsock6 = INVALID_SOCKET;
1075
1076#ifndef RT_OS_DARWIN
1077 const int icmpstype = SOCK_RAW;
1078#else
1079 /* on OS X it's not privileged */
1080 const int icmpstype = SOCK_DGRAM;
1081#endif
1082
1083 icmpsock6 = socket(AF_INET6, icmpstype, IPPROTO_ICMPV6);
1084 if (icmpsock6 == INVALID_SOCKET)
1085 {
1086 perror("IPPROTO_ICMPV6");
1087#ifdef VBOX_RAWSOCK_DEBUG_HELPER
1088 icmpsock6 = getrawsock(AF_INET6);
1089#endif
1090 }
1091
1092 if (icmpsock6 != INVALID_SOCKET)
1093 {
1094#ifdef ICMP6_FILTER // Windows doesn't support RFC 3542 API
1095 /*
1096 * XXX: We do this here for now, not in pxping.c, to avoid
1097 * name clashes between lwIP and system headers.
1098 */
1099 struct icmp6_filter flt;
1100 ICMP6_FILTER_SETBLOCKALL(&flt);
1101
1102 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &flt);
1103
1104 ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &flt);
1105 ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &flt);
1106 ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &flt);
1107 ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &flt);
1108
1109 int status = setsockopt(icmpsock6, IPPROTO_ICMPV6, ICMP6_FILTER,
1110 &flt, sizeof(flt));
1111 if (status < 0)
1112 {
1113 perror("ICMP6_FILTER");
1114 }
1115#endif
1116 }
1117
1118 m_ProxyOptions.icmpsock6 = icmpsock6;
1119}
1120#endif
1121
1122
1123/**
1124 * Adapter for the ListenerImpl template. It has to be a separate
1125 * object because ListenerImpl deletes it. Just a small wrapper that
1126 * delegates the real work back to VBoxNetSlirpNAT.
1127 */
1128class VBoxNetSlirpNAT::Listener::Adapter
1129{
1130 VBoxNetSlirpNAT *m_pNAT;
1131public:
1132 Adapter() : m_pNAT(NULL) {}
1133 HRESULT init() { return init(NULL); }
1134 void uninit() { m_pNAT = NULL; }
1135
1136 HRESULT init(VBoxNetSlirpNAT *pNAT)
1137 {
1138 m_pNAT = pNAT;
1139 return S_OK;
1140 }
1141
1142 HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1143 {
1144 if (RT_LIKELY(m_pNAT != NULL))
1145 return m_pNAT->HandleEvent(aEventType, pEvent);
1146 else
1147 return S_OK;
1148 }
1149};
1150
1151
1152HRESULT
1153VBoxNetSlirpNAT::Listener::init(VBoxNetSlirpNAT *pNAT)
1154{
1155 HRESULT hrc;
1156
1157 hrc = m_pListenerImpl.createObject();
1158 if (FAILED(hrc))
1159 return hrc;
1160
1161 hrc = m_pListenerImpl->init(new Adapter(), pNAT);
1162 if (FAILED(hrc))
1163 {
1164 VBoxNetSlirpNAT::reportComError(m_pListenerImpl, "init", hrc);
1165 return hrc;
1166 }
1167
1168 return hrc;
1169}
1170
1171
1172void
1173VBoxNetSlirpNAT::Listener::uninit()
1174{
1175 unlisten();
1176 m_pListenerImpl.setNull();
1177}
1178
1179
1180/*
1181 * There's no base interface that exposes "eventSource" so fake it
1182 * with a template.
1183 */
1184template <typename IEventful>
1185HRESULT
1186VBoxNetSlirpNAT::Listener::listen(const ComPtr<IEventful> &pEventful,
1187 const VBoxEventType_T aEvents[])
1188{
1189 HRESULT hrc;
1190
1191 if (m_pListenerImpl.isNull())
1192 return S_OK;
1193
1194 ComPtr<IEventSource> pEventSource;
1195 hrc = pEventful->COMGETTER(EventSource)(pEventSource.asOutParam());
1196 if (FAILED(hrc))
1197 {
1198 VBoxNetSlirpNAT::reportComError(pEventful, "EventSource", hrc);
1199 return hrc;
1200 }
1201
1202 /* got a real interface, punt to the non-template code */
1203 hrc = doListen(pEventSource, aEvents);
1204 if (FAILED(hrc))
1205 return hrc;
1206
1207 return hrc;
1208}
1209
1210
1211HRESULT
1212VBoxNetSlirpNAT::Listener::doListen(const ComPtr<IEventSource> &pEventSource,
1213 const VBoxEventType_T aEvents[])
1214{
1215 HRESULT hrc;
1216
1217 com::SafeArray<VBoxEventType_T> aInteresting;
1218 for (size_t i = 0; aEvents[i] != VBoxEventType_Invalid; ++i)
1219 aInteresting.push_back(aEvents[i]);
1220
1221 BOOL fActive = true;
1222 hrc = pEventSource->RegisterListener(m_pListenerImpl,
1223 ComSafeArrayAsInParam(aInteresting),
1224 fActive);
1225 if (FAILED(hrc))
1226 {
1227 VBoxNetSlirpNAT::reportComError(m_pEventSource, "RegisterListener", hrc);
1228 return hrc;
1229 }
1230
1231 m_pEventSource = pEventSource;
1232 return hrc;
1233}
1234
1235
1236HRESULT
1237VBoxNetSlirpNAT::Listener::unlisten()
1238{
1239 HRESULT hrc;
1240
1241 if (m_pEventSource.isNull())
1242 return S_OK;
1243
1244 const ComPtr<IEventSource> pEventSource = m_pEventSource;
1245 m_pEventSource.setNull();
1246
1247 hrc = pEventSource->UnregisterListener(m_pListenerImpl);
1248 if (FAILED(hrc))
1249 {
1250 VBoxNetSlirpNAT::reportComError(pEventSource, "UnregisterListener", hrc);
1251 return hrc;
1252 }
1253
1254 return hrc;
1255}
1256
1257
1258
1259/**
1260 * Create and register API event listeners.
1261 */
1262int VBoxNetSlirpNAT::initComEvents()
1263{
1264 /**
1265 * @todo r=uwe These events are reported on both IVirtualBox and
1266 * INATNetwork objects. We used to listen for them on our
1267 * network, but it was changed later to listen on vbox. Leave it
1268 * that way for now. Note that HandleEvent() has to do additional
1269 * check for them to ignore events for other networks.
1270 */
1271 static const VBoxEventType_T s_aNATNetEvents[] = {
1272 VBoxEventType_OnNATNetworkPortForward,
1273 VBoxEventType_OnNATNetworkSetting,
1274 VBoxEventType_Invalid
1275 };
1276 m_ListenerNATNet.init(this);
1277 m_ListenerNATNet.listen(virtualbox, s_aNATNetEvents); // sic!
1278
1279 static const VBoxEventType_T s_aVirtualBoxEvents[] = {
1280 VBoxEventType_OnHostNameResolutionConfigurationChange,
1281 VBoxEventType_OnNATNetworkStartStop,
1282 VBoxEventType_Invalid
1283 };
1284 m_ListenerVirtualBox.init(this);
1285 m_ListenerVirtualBox.listen(virtualbox, s_aVirtualBoxEvents);
1286
1287 static const VBoxEventType_T s_aVBoxClientEvents[] = {
1288 VBoxEventType_OnVBoxSVCAvailabilityChanged,
1289 VBoxEventType_Invalid
1290 };
1291 m_ListenerVBoxClient.init(this);
1292 m_ListenerVBoxClient.listen(virtualboxClient, s_aVBoxClientEvents);
1293
1294 return VINF_SUCCESS;
1295}
1296
1297
1298/**
1299 * Run the pumps.
1300 *
1301 * Spawn the intnet pump thread that gets packets from the intnet and
1302 * feeds them to lwIP. Enter COM event loop here, on the main thread.
1303 */
1304int
1305VBoxNetSlirpNAT::run()
1306{
1307 AssertReturn(m_hThrRecv == NIL_RTTHREAD, VERR_INVALID_STATE);
1308 AssertReturn(m_hThrdPoll == NIL_RTTHREAD, VERR_INVALID_STATE);
1309
1310 /* Spawn the I/O polling thread. */
1311 int rc = RTThreadCreate(&m_hThrdPoll,
1312 VBoxNetSlirpNAT::pollThread, this,
1313 0, /* :cbStack */
1314 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1315 "Poll");
1316 AssertRCReturn(rc, rc);
1317
1318 /* spawn intnet input pump */
1319 rc = RTThreadCreate(&m_hThrRecv,
1320 VBoxNetSlirpNAT::receiveThread, this,
1321 0, /* :cbStack */
1322 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1323 "RECV");
1324 AssertRCReturn(rc, rc);
1325
1326 /* main thread will run the API event queue pump */
1327 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1328 if (pQueue == NULL)
1329 {
1330 LogRel(("run: getMainEventQueue() == NULL\n"));
1331 return VERR_GENERAL_FAILURE;
1332 }
1333
1334 /* dispatch API events to our listeners */
1335 for (;;)
1336 {
1337 rc = pQueue->processEventQueue(RT_INDEFINITE_WAIT);
1338 if (rc == VERR_INTERRUPTED)
1339 {
1340 LogRel(("run: shutdown\n"));
1341 break;
1342 }
1343 else if (rc != VINF_SUCCESS)
1344 {
1345 /* note any unexpected rc */
1346 LogRel(("run: processEventQueue: %Rrc\n", rc));
1347 }
1348 }
1349
1350 /*
1351 * We are out of the event loop, so we were told to shut down.
1352 * Tell other threads to wrap up.
1353 */
1354
1355 /* tell the intnet input pump to terminate */
1356 IntNetR3IfWaitAbort(m_hIf);
1357
1358 rc = RTThreadWait(m_hThrRecv, 5000, NULL);
1359 m_hThrRecv = NIL_RTTHREAD;
1360
1361 return rc;
1362}
1363
1364
1365void
1366VBoxNetSlirpNAT::shutdown()
1367{
1368 int rc;
1369
1370 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1371 if (pQueue == NULL)
1372 {
1373 LogRel(("shutdown: getMainEventQueue() == NULL\n"));
1374 return;
1375 }
1376
1377 /* unregister listeners */
1378 m_ListenerNATNet.unlisten();
1379 m_ListenerVirtualBox.unlisten();
1380 m_ListenerVBoxClient.unlisten();
1381
1382 /* tell the event loop in run() to stop */
1383 rc = pQueue->interruptEventQueueProcessing();
1384 if (RT_FAILURE(rc))
1385 LogRel(("shutdown: interruptEventQueueProcessing: %Rrc\n", rc));
1386}
1387
1388
1389/**
1390 * @note: this work on Event thread.
1391 */
1392HRESULT VBoxNetSlirpNAT::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1393{
1394 HRESULT hrc = S_OK;
1395 switch (aEventType)
1396 {
1397 case VBoxEventType_OnNATNetworkSetting:
1398 {
1399 ComPtr<INATNetworkSettingEvent> pSettingsEvent(pEvent);
1400
1401 com::Bstr networkName;
1402 hrc = pSettingsEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1403 AssertComRCReturn(hrc, hrc);
1404 if (networkName != m_strNetworkName)
1405 break; /* change not for our network */
1406
1407 // XXX: only handle IPv6 default route for now
1408 if (!m_ProxyOptions.in6_enabled)
1409 break;
1410
1411 BOOL fIPv6DefaultRoute = FALSE;
1412 hrc = pSettingsEvent->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
1413 AssertComRCReturn(hrc, hrc);
1414
1415#if 0 /** @todo */
1416 if (m_ProxyOptions.ipv6_defroute == fIPv6DefaultRoute)
1417 break;
1418
1419 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
1420 tcpip_callback_with_block(proxy_rtadvd_do_quick, &m_LwipNetIf, 0);
1421#endif
1422 break;
1423 }
1424
1425 case VBoxEventType_OnNATNetworkPortForward:
1426 {
1427 int rc = VINF_SUCCESS;
1428 ComPtr<INATNetworkPortForwardEvent> pForwardEvent = pEvent;
1429
1430 com::Bstr networkName;
1431 hrc = pForwardEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1432 AssertComRCReturn(hrc, hrc);
1433 if (networkName != m_strNetworkName)
1434 break; /* change not for our network */
1435
1436 BOOL fCreateFW;
1437 hrc = pForwardEvent->COMGETTER(Create)(&fCreateFW);
1438 AssertComRCReturn(hrc, hrc);
1439
1440 BOOL fIPv6FW;
1441 hrc = pForwardEvent->COMGETTER(Ipv6)(&fIPv6FW);
1442 AssertComRCReturn(hrc, hrc);
1443
1444 com::Bstr name;
1445 hrc = pForwardEvent->COMGETTER(Name)(name.asOutParam());
1446 AssertComRCReturn(hrc, hrc);
1447
1448 NATProtocol_T proto = NATProtocol_TCP;
1449 hrc = pForwardEvent->COMGETTER(Proto)(&proto);
1450 AssertComRCReturn(hrc, hrc);
1451
1452 com::Bstr strHostAddr;
1453 hrc = pForwardEvent->COMGETTER(HostIp)(strHostAddr.asOutParam());
1454 AssertComRCReturn(hrc, hrc);
1455
1456 LONG lHostPort;
1457 hrc = pForwardEvent->COMGETTER(HostPort)(&lHostPort);
1458 AssertComRCReturn(hrc, hrc);
1459
1460 com::Bstr strGuestAddr;
1461 hrc = pForwardEvent->COMGETTER(GuestIp)(strGuestAddr.asOutParam());
1462 AssertComRCReturn(hrc, hrc);
1463
1464 LONG lGuestPort;
1465 hrc = pForwardEvent->COMGETTER(GuestPort)(&lGuestPort);
1466 AssertComRCReturn(hrc, hrc);
1467
1468 PPORTFORWARDRULE pNatPf = (PPORTFORWARDRULE)RTMemAllocZ(sizeof(*pNatPf));
1469 if (!pNatPf)
1470 {
1471 hrc = E_OUTOFMEMORY;
1472 goto port_forward_done;
1473 }
1474
1475 pNatPf->fPfrIPv6 = fIPv6FW;
1476
1477 switch (proto)
1478 {
1479 case NATProtocol_TCP:
1480 pNatPf->iPfrProto = IPPROTO_TCP;
1481 break;
1482 case NATProtocol_UDP:
1483 pNatPf->iPfrProto = IPPROTO_UDP;
1484 break;
1485
1486 default:
1487 LogRel(("Event: %s %s port-forwarding rule \"%s\": invalid protocol %d\n",
1488 fCreateFW ? "Add" : "Remove",
1489 fIPv6FW ? "IPv6" : "IPv4",
1490 com::Utf8Str(name).c_str(),
1491 (int)proto));
1492 goto port_forward_done;
1493 }
1494
1495 LogRel(("Event: %s %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1496 fCreateFW ? "Add" : "Remove",
1497 fIPv6FW ? "IPv6" : "IPv4",
1498 com::Utf8Str(name).c_str(),
1499 proto == NATProtocol_TCP ? "TCP" : "UDP",
1500 /* from */
1501 fIPv6FW ? "[" : "",
1502 com::Utf8Str(strHostAddr).c_str(),
1503 fIPv6FW ? "]" : "",
1504 lHostPort,
1505 /* to */
1506 fIPv6FW ? "[" : "",
1507 com::Utf8Str(strGuestAddr).c_str(),
1508 fIPv6FW ? "]" : "",
1509 lGuestPort));
1510
1511 if (name.length() > sizeof(pNatPf->szPfrName))
1512 {
1513 hrc = E_INVALIDARG;
1514 goto port_forward_done;
1515 }
1516
1517 RTStrPrintf(pNatPf->szPfrName, sizeof(pNatPf->szPfrName),
1518 "%s", com::Utf8Str(name).c_str());
1519
1520 RTStrPrintf(pNatPf->szPfrHostAddr, sizeof(pNatPf->szPfrHostAddr),
1521 "%s", com::Utf8Str(strHostAddr).c_str());
1522
1523 /* XXX: limits should be checked */
1524 pNatPf->u16PfrHostPort = (uint16_t)lHostPort;
1525
1526 RTStrPrintf(pNatPf->szPfrGuestAddr, sizeof(pNatPf->szPfrGuestAddr),
1527 "%s", com::Utf8Str(strGuestAddr).c_str());
1528
1529 /* XXX: limits should be checked */
1530 pNatPf->u16PfrGuestPort = (uint16_t)lGuestPort;
1531
1532 rc = RTReqQueueCallEx(m_hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
1533 (PFNRT)natServicePfRegister, 4, this, pNatPf, !fCreateFW /*fRemove*/, true /*fRuntime*/);
1534 if (RT_FAILURE(rc))
1535 RTMemFree(pNatPf);
1536
1537 port_forward_done:
1538 /* clean up strings */
1539 name.setNull();
1540 strHostAddr.setNull();
1541 strGuestAddr.setNull();
1542 break;
1543 }
1544
1545 case VBoxEventType_OnHostNameResolutionConfigurationChange:
1546 {
1547#if 0 /** @todo */
1548 const char **ppcszNameServers = getHostNameservers();
1549 err_t error;
1550
1551 error = tcpip_callback_with_block(pxdns_set_nameservers,
1552 ppcszNameServers,
1553 /* :block */ 0);
1554 if (error != ERR_OK && ppcszNameServers != NULL)
1555 RTMemFree(ppcszNameServers);
1556#endif
1557 break;
1558 }
1559
1560 case VBoxEventType_OnNATNetworkStartStop:
1561 {
1562 ComPtr <INATNetworkStartStopEvent> pStartStopEvent = pEvent;
1563
1564 com::Bstr networkName;
1565 hrc = pStartStopEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1566 AssertComRCReturn(hrc, hrc);
1567 if (networkName != m_strNetworkName)
1568 break; /* change not for our network */
1569
1570 BOOL fStart = TRUE;
1571 hrc = pStartStopEvent->COMGETTER(StartEvent)(&fStart);
1572 AssertComRCReturn(hrc, hrc);
1573
1574 if (!fStart)
1575 shutdown();
1576 break;
1577 }
1578
1579 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
1580 {
1581 LogRel(("VBoxSVC became unavailable, exiting.\n"));
1582 shutdown();
1583 break;
1584 }
1585
1586 default: break; /* Shut up MSC. */
1587 }
1588 return hrc;
1589}
1590
1591
1592/**
1593 * Read the list of host's resolvers via the API.
1594 *
1595 * Called during initialization and in response to the
1596 * VBoxEventType_OnHostNameResolutionConfigurationChange event.
1597 */
1598const char **VBoxNetSlirpNAT::getHostNameservers()
1599{
1600 if (m_host.isNull())
1601 return NULL;
1602
1603 com::SafeArray<BSTR> aNameServers;
1604 HRESULT hrc = m_host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(aNameServers));
1605 if (FAILED(hrc))
1606 return NULL;
1607
1608 const size_t cNameServers = aNameServers.size();
1609 if (cNameServers == 0)
1610 return NULL;
1611
1612 const char **ppcszNameServers =
1613 (const char **)RTMemAllocZ(sizeof(char *) * (cNameServers + 1));
1614 if (ppcszNameServers == NULL)
1615 return NULL;
1616
1617 size_t idxLast = 0;
1618 for (size_t i = 0; i < cNameServers; ++i)
1619 {
1620 com::Utf8Str strNameServer(aNameServers[i]);
1621 ppcszNameServers[idxLast] = RTStrDup(strNameServer.c_str());
1622 if (ppcszNameServers[idxLast] != NULL)
1623 ++idxLast;
1624 }
1625
1626 if (idxLast == 0)
1627 {
1628 RTMemFree(ppcszNameServers);
1629 return NULL;
1630 }
1631
1632 return ppcszNameServers;
1633}
1634
1635
1636/**
1637 * Fetch port-forwarding rules via the API.
1638 *
1639 * Reads the initial sets of rules from VBoxSVC. The rules will be
1640 * activated when all the initialization and plumbing is done. See
1641 * natServiceProcessRegisteredPf().
1642 */
1643int VBoxNetSlirpNAT::fetchNatPortForwardRules(VECNATSERVICEPF &vec, bool fIsIPv6)
1644{
1645 HRESULT hrc;
1646
1647 com::SafeArray<BSTR> rules;
1648 if (fIsIPv6)
1649 hrc = m_net->COMGETTER(PortForwardRules6)(ComSafeArrayAsOutParam(rules));
1650 else
1651 hrc = m_net->COMGETTER(PortForwardRules4)(ComSafeArrayAsOutParam(rules));
1652 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
1653
1654 NATSERVICEPORTFORWARDRULE Rule;
1655 for (size_t idxRules = 0; idxRules < rules.size(); ++idxRules)
1656 {
1657 Log(("%d-%s rule: %ls\n", idxRules, (fIsIPv6 ? "IPv6" : "IPv4"), rules[idxRules]));
1658 RT_ZERO(Rule);
1659
1660 int rc = netPfStrToPf(com::Utf8Str(rules[idxRules]).c_str(), fIsIPv6,
1661 &Rule.Pfr);
1662 if (RT_FAILURE(rc))
1663 continue;
1664
1665 vec.push_back(Rule);
1666 }
1667
1668 return VINF_SUCCESS;
1669}
1670
1671
1672/**
1673 * Activate the initial set of port-forwarding rules.
1674 */
1675int VBoxNetSlirpNAT::natServiceProcessRegisteredPf(VECNATSERVICEPF& vecRules)
1676{
1677 ITERATORNATSERVICEPF it;
1678 for (it = vecRules.begin(); it != vecRules.end(); ++it)
1679 {
1680 NATSERVICEPORTFORWARDRULE &natPf = *it;
1681
1682 LogRel(("Loading %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1683 natPf.Pfr.fPfrIPv6 ? "IPv6" : "IPv4",
1684 natPf.Pfr.szPfrName,
1685 natPf.Pfr.iPfrProto == IPPROTO_TCP ? "TCP" : "UDP",
1686 /* from */
1687 natPf.Pfr.fPfrIPv6 ? "[" : "",
1688 natPf.Pfr.szPfrHostAddr,
1689 natPf.Pfr.fPfrIPv6 ? "]" : "",
1690 natPf.Pfr.u16PfrHostPort,
1691 /* to */
1692 natPf.Pfr.fPfrIPv6 ? "[" : "",
1693 natPf.Pfr.szPfrGuestAddr,
1694 natPf.Pfr.fPfrIPv6 ? "]" : "",
1695 natPf.Pfr.u16PfrGuestPort));
1696
1697 natServicePfRegister(this, &natPf.Pfr, false /*fRemove*/, false /*fRuntime*/);
1698 }
1699
1700 return VINF_SUCCESS;
1701}
1702
1703
1704/**
1705 * Activate a single port-forwarding rule.
1706 *
1707 * This is used both when we activate all the initial rules on startup
1708 * and when port-forwarding rules are changed and we are notified via
1709 * an API event.
1710 */
1711/* static */
1712DECLCALLBACK(void) VBoxNetSlirpNAT::natServicePfRegister(VBoxNetSlirpNAT *pThis, PPORTFORWARDRULE pNatPf, bool fRemove, bool fRuntime)
1713{
1714 bool fUdp;
1715 switch(pNatPf->iPfrProto)
1716 {
1717 case IPPROTO_TCP:
1718 fUdp = false;
1719 break;
1720 case IPPROTO_UDP:
1721 fUdp = true;
1722 break;
1723 default:
1724 return;
1725 }
1726
1727 const char *pszHostAddr = pNatPf->szPfrHostAddr;
1728 if (pszHostAddr[0] == '\0')
1729 {
1730 if (pNatPf->fPfrIPv6)
1731 pszHostAddr = "::";
1732 else
1733 pszHostAddr = "0.0.0.0";
1734 }
1735
1736 const char *pszGuestAddr = pNatPf->szPfrGuestAddr;
1737 if (pszGuestAddr[0] == '\0')
1738 {
1739 if (pNatPf->fPfrIPv6)
1740 pszGuestAddr = "::";
1741 else
1742 pszGuestAddr = "0.0.0.0";
1743 }
1744
1745 struct in_addr guestIp, hostIp;
1746 if (inet_aton(pszHostAddr, &hostIp) == 0)
1747 hostIp.s_addr = INADDR_ANY;
1748
1749 if (inet_aton(pszGuestAddr, &guestIp) == 0)
1750 {
1751 LogRel(("Unable to convert guest address '%s' for %s rule \"%s\"\n",
1752 pszGuestAddr, pNatPf->fPfrIPv6 ? "IPv6" : "IPv4",
1753 pNatPf->szPfrName));
1754 return;
1755 }
1756
1757 int rc;
1758 if (fRemove)
1759 rc = slirp_remove_hostfwd(pThis->m_pSlirp, fUdp, hostIp, pNatPf->u16PfrHostPort);
1760 else
1761 rc = slirp_add_hostfwd(pThis->m_pSlirp, fUdp,
1762 hostIp, pNatPf->u16PfrHostPort,
1763 guestIp, pNatPf->u16PfrGuestPort);
1764 if (!rc)
1765 {
1766 if (fRuntime)
1767 {
1768 VECNATSERVICEPF& rules = pNatPf->fPfrIPv6 ? pThis->m_vecPortForwardRule6
1769 : pThis->m_vecPortForwardRule4;
1770 if (fRemove)
1771 {
1772 ITERATORNATSERVICEPF it;
1773 for (it = rules.begin(); it != rules.end(); ++it)
1774 {
1775 /* compare */
1776 NATSERVICEPORTFORWARDRULE &natFw = *it;
1777 if ( natFw.Pfr.iPfrProto == pNatPf->iPfrProto
1778 && natFw.Pfr.u16PfrHostPort == pNatPf->u16PfrHostPort
1779 && strncmp(natFw.Pfr.szPfrHostAddr, pNatPf->szPfrHostAddr, INET6_ADDRSTRLEN) == 0
1780 && natFw.Pfr.u16PfrGuestPort == pNatPf->u16PfrGuestPort
1781 && strncmp(natFw.Pfr.szPfrGuestAddr, pNatPf->szPfrGuestAddr, INET6_ADDRSTRLEN) == 0)
1782 {
1783 rules.erase(it);
1784 break;
1785 }
1786 } /* loop over vector elements */
1787 }
1788 else /* Addition */
1789 {
1790 NATSERVICEPORTFORWARDRULE r;
1791 RT_ZERO(r);
1792 memcpy(&r.Pfr, pNatPf, sizeof(*pNatPf));
1793 rules.push_back(r);
1794 } /* condition add or delete */
1795 }
1796 else /* The rules vector is already up to date. */
1797 Assert(fRemove == false);
1798 }
1799 else
1800 LogRel(("Unable to %s %s rule \"%s\"\n",
1801 fRemove ? "remove" : "add",
1802 pNatPf->fPfrIPv6 ? "IPv6" : "IPv4",
1803 pNatPf->szPfrName));
1804
1805 /* Free the rule in any case. */
1806 RTMemFree(pNatPf);
1807}
1808
1809
1810/**
1811 * Converts slirp representation of poll events to host representation.
1812 *
1813 * @param iEvents Integer representing slirp type poll events.
1814 *
1815 * @returns Integer representing host type poll events.
1816 */
1817DECLINLINE(short) pollEventSlirpToHost(int iEvents)
1818{
1819 short iRet = 0;
1820#ifndef RT_OS_WINDOWS
1821 if (iEvents & SLIRP_POLL_IN) iRet |= POLLIN;
1822 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLOUT;
1823 if (iEvents & SLIRP_POLL_PRI) iRet |= POLLPRI;
1824 if (iEvents & SLIRP_POLL_ERR) iRet |= POLLERR;
1825 if (iEvents & SLIRP_POLL_HUP) iRet |= POLLHUP;
1826#else
1827 if (iEvents & SLIRP_POLL_IN) iRet |= (POLLRDNORM | POLLRDBAND);
1828 if (iEvents & SLIRP_POLL_OUT) iRet |= POLLWRNORM;
1829 if (iEvents & SLIRP_POLL_PRI) iRet |= (POLLIN);
1830 if (iEvents & SLIRP_POLL_ERR) iRet |= 0;
1831 if (iEvents & SLIRP_POLL_HUP) iRet |= 0;
1832#endif
1833 return iRet;
1834}
1835
1836/**
1837 * Converts host representation of poll events to slirp representation.
1838 *
1839 * @param iEvents Integer representing host type poll events.
1840 *
1841 * @returns Integer representing slirp type poll events.
1842 *
1843 * @thread ?
1844 */
1845DECLINLINE(int) pollEventHostToSlirp(int iEvents)
1846{
1847 int iRet = 0;
1848#ifndef RT_OS_WINDOWS
1849 if (iEvents & POLLIN) iRet |= SLIRP_POLL_IN;
1850 if (iEvents & POLLOUT) iRet |= SLIRP_POLL_OUT;
1851 if (iEvents & POLLPRI) iRet |= SLIRP_POLL_PRI;
1852 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
1853 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
1854#else
1855 if (iEvents & (POLLRDNORM | POLLRDBAND)) iRet |= SLIRP_POLL_IN;
1856 if (iEvents & POLLWRNORM) iRet |= SLIRP_POLL_OUT;
1857 if (iEvents & (POLLPRI)) iRet |= SLIRP_POLL_PRI;
1858 if (iEvents & POLLERR) iRet |= SLIRP_POLL_ERR;
1859 if (iEvents & POLLHUP) iRet |= SLIRP_POLL_HUP;
1860#endif
1861 return iRet;
1862}
1863
1864
1865/*
1866 * Libslirp Callbacks
1867 */
1868/**
1869 * Get the NAT thread out of poll/WSAWaitForMultipleEvents
1870 */
1871void VBoxNetSlirpNAT::slirpNotifyPollThread(const char *pszWho)
1872{
1873 RT_NOREF(pszWho);
1874#ifdef RT_OS_WINDOWS
1875 int cbWritten = send(m_ahWakeupSockPair[0], "", 1, NULL);
1876 if (RT_LIKELY(cbWritten != SOCKET_ERROR))
1877 ASMAtomicIncU64(&m_cWakeupNotifs);
1878 else
1879 Log4(("Notify NAT Thread Error %d\n", WSAGetLastError()));
1880#else
1881 /* kick poll() */
1882 size_t cbIgnored;
1883 int rc = RTPipeWrite(m_hPipeWrite, "", 1, &cbIgnored);
1884 AssertRC(rc);
1885 if (RT_SUCCESS(rc))
1886 ASMAtomicIncU64(&m_cWakeupNotifs);
1887#endif
1888}
1889
1890
1891/**
1892 * Callback called by libslirp to send packet into the internal network.
1893 *
1894 * @param pvBuf Pointer to packet buffer.
1895 * @param cb Size of packet.
1896 * @param pvUser Pointer to NAT State context.
1897 *
1898 * @returns Size of packet received or -1 on error.
1899 *
1900 * @thread ?
1901 */
1902/*static*/ ssize_t VBoxNetSlirpNAT::slirpSendPacketCb(const void *pvBuf, ssize_t cb, void *pvUser) RT_NOTHROW_DEF
1903{
1904 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
1905 AssertPtrReturn(pThis, -1);
1906
1907 INTNETFRAME Frame;
1908 int rc = IntNetR3IfQueryOutputFrame(pThis->m_hIf, (uint32_t)cb, &Frame);
1909 if (RT_FAILURE(rc))
1910 return -1;
1911
1912 memcpy(Frame.pvFrame, pvBuf, cb);
1913 rc = IntNetR3IfOutputFrameCommit(pThis->m_hIf, &Frame);
1914 if (RT_FAILURE(rc))
1915 return -1;
1916 return cb;
1917}
1918
1919
1920/**
1921 * Callback called by libslirp when the guest does something wrong.
1922 *
1923 * @param pszMsg Error message string.
1924 * @param pvUser Pointer to NAT State context.
1925 *
1926 * @thread ?
1927 */
1928/*static*/ void VBoxNetSlirpNAT::slirpGuestErrorCb(const char *pszMsg, void *pvUser) RT_NOTHROW_DEF
1929{
1930 /* Note! This is _just_ libslirp complaining about odd guest behaviour. */
1931 LogRelMax(250, ("NAT Guest Error: %s\n", pszMsg));
1932 RT_NOREF(pvUser);
1933}
1934
1935
1936/**
1937 * Callback called by libslirp to get the current timestamp in nanoseconds.
1938 *
1939 * @param pvUser Pointer to NAT State context.
1940 *
1941 * @returns 64-bit signed integer representing time in nanoseconds.
1942 */
1943/*static*/ int64_t VBoxNetSlirpNAT::slirpClockGetNsCb(void *pvUser) RT_NOTHROW_DEF
1944{
1945 RT_NOREF(pvUser);
1946 return (int64_t)RTTimeNanoTS();
1947}
1948
1949/**
1950 * Callback called by slirp to create a new timer and insert it into the given list.
1951 *
1952 * @param slirpTimeCb Callback function supplied to the new timer upon timer expiry.
1953 * Called later by the timeout handler.
1954 * @param cb_opaque Opaque object supplied to slirpTimeCb when called. Should be
1955 * Identical to the opaque parameter.
1956 * @param opaque Pointer to NAT State context.
1957 *
1958 * @returns Pointer to new timer.
1959 */
1960/*static*/ void *VBoxNetSlirpNAT::slirpTimerNewCb(SlirpTimerCb slirpTimeCb, void *cb_opaque, void *opaque) RT_NOTHROW_DEF
1961{
1962 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
1963 Assert(pThis);
1964
1965 SlirpTimer * const pNewTimer = (SlirpTimer *)RTMemAlloc(sizeof(*pNewTimer));
1966 if (pNewTimer)
1967 {
1968 pNewTimer->msExpire = 0;
1969 pNewTimer->pHandler = slirpTimeCb;
1970 pNewTimer->opaque = cb_opaque;
1971 /** @todo r=bird: Not thread safe. Assumes pSlirpThread */
1972 pNewTimer->next = pThis->pTimerHead;
1973 pThis->pTimerHead = pNewTimer;
1974 }
1975 return pNewTimer;
1976}
1977
1978/**
1979 * Callback called by slirp to free a timer.
1980 *
1981 * @param pvTimer Pointer to slirpTimer object to be freed.
1982 * @param pvUser Pointer to NAT State context.
1983 */
1984/*static*/ void VBoxNetSlirpNAT::slirpTimerFreeCb(void *pvTimer, void *pvUser) RT_NOTHROW_DEF
1985{
1986 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
1987 SlirpTimer * const pTimer = (SlirpTimer *)pvTimer;
1988 Assert(pThis);
1989
1990 SlirpTimer *pPrev = NULL;
1991 SlirpTimer *pCurrent = pThis->pTimerHead;
1992 while (pCurrent != NULL)
1993 {
1994 if (pCurrent == pTimer)
1995 {
1996 /* unlink it. */
1997 if (!pPrev)
1998 pThis->pTimerHead = pCurrent->next;
1999 else
2000 pPrev->next = pCurrent->next;
2001 pCurrent->next = NULL;
2002 RTMemFree(pCurrent);
2003 return;
2004 }
2005
2006 /* advance */
2007 pPrev = pCurrent;
2008 pCurrent = pCurrent->next;
2009 }
2010 Assert(!pTimer);
2011}
2012
2013/**
2014 * Callback called by slirp to modify a timer.
2015 *
2016 * @param pvTimer Pointer to slirpTimer object to be modified.
2017 * @param msNewDeadlineTs The new absolute expiration time in milliseconds.
2018 * Zero stops it.
2019 * @param pvUser Pointer to NAT State context.
2020 */
2021/*static*/ void VBoxNetSlirpNAT::slirpTimerModCb(void *pvTimer, int64_t msNewDeadlineTs, void *pvUser) RT_NOTHROW_DEF
2022{
2023 SlirpTimer * const pTimer = (SlirpTimer *)pvTimer;
2024 pTimer->msExpire = msNewDeadlineTs;
2025 RT_NOREF(pvUser);
2026}
2027
2028/**
2029 * Callback called by slirp when there is I/O that needs to happen.
2030 *
2031 * @param opaque Pointer to NAT State context.
2032 */
2033/*static*/ void VBoxNetSlirpNAT::slirpNotifyCb(void *opaque) RT_NOTHROW_DEF
2034{
2035 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2036 RT_NOREF(pThis);
2037 //drvNATNotifyNATThread(pThis, "drvNAT_NotifyCb");
2038}
2039
2040/**
2041 * Registers poll. Unused function (other than logging).
2042 */
2043/*static*/ void VBoxNetSlirpNAT::slirpRegisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_DEF
2044{
2045 RT_NOREF(socket, opaque);
2046#ifdef RT_OS_WINDOWS
2047 Log4(("Poll registered: fd=%p\n", socket));
2048#else
2049 Log4(("Poll registered: fd=%d\n", socket));
2050#endif
2051}
2052
2053/**
2054 * Unregisters poll. Unused function (other than logging).
2055 */
2056/*static*/ void VBoxNetSlirpNAT::slirpUnregisterPoll(slirp_os_socket socket, void *opaque) RT_NOTHROW_DEF
2057{
2058 RT_NOREF(socket, opaque);
2059#ifdef RT_OS_WINDOWS
2060 Log4(("Poll unregistered: fd=%p\n", socket));
2061#else
2062 Log4(("Poll unregistered: fd=%d\n", socket));
2063#endif
2064}
2065
2066/**
2067 * Callback function to add entry to pollfd array.
2068 *
2069 * @param hFd Socket handle.
2070 * @param iEvents Integer of slirp type poll events.
2071 * @param opaque Pointer to NAT State context.
2072 *
2073 * @returns Index of latest pollfd entry.
2074 *
2075 * @thread ?
2076 */
2077/*static*/ int VBoxNetSlirpNAT::slirpAddPollCb(slirp_os_socket hFd, int iEvents, void *opaque) RT_NOTHROW_DEF
2078{
2079 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2080
2081 if (pThis->nsock + 1 >= pThis->uPollCap)
2082 {
2083 size_t cbNew = pThis->uPollCap * 2 * sizeof(struct pollfd);
2084 struct pollfd *pvNew = (struct pollfd *)RTMemRealloc(pThis->polls, cbNew);
2085 if (pvNew)
2086 {
2087 pThis->polls = pvNew;
2088 pThis->uPollCap *= 2;
2089 }
2090 else
2091 return -1;
2092 }
2093
2094 unsigned int uIdx = pThis->nsock;
2095 Assert(uIdx < INT_MAX);
2096 pThis->polls[uIdx].fd = hFd;
2097 pThis->polls[uIdx].events = pollEventSlirpToHost(iEvents);
2098 pThis->polls[uIdx].revents = 0;
2099 pThis->nsock += 1;
2100 return uIdx;
2101}
2102
2103/**
2104 * Get translated revents from a poll at a given index.
2105 *
2106 * @param idx Integer index of poll.
2107 * @param opaque Pointer to NAT State context.
2108 *
2109 * @returns Integer representing transalted revents.
2110 *
2111 * @thread ?
2112 */
2113/*static*/ int VBoxNetSlirpNAT::slirpGetREventsCb(int idx, void *opaque) RT_NOTHROW_DEF
2114{
2115 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(opaque);
2116 struct pollfd* polls = pThis->polls;
2117 return pollEventHostToSlirp(polls[idx].revents);
2118}
2119
2120
2121/**
2122 * Run expired timers.
2123 *
2124 * @thread pSlirpThread
2125 */
2126void VBoxNetSlirpNAT::timersRunExpired(void)
2127{
2128 int64_t const msNow = slirpClockGetNsCb(this) / RT_NS_1MS;
2129 SlirpTimer *pCurrent = pTimerHead;
2130 while (pCurrent != NULL)
2131 {
2132 SlirpTimer * const pNext = pCurrent->next; /* (in case the timer is destroyed from the callback) */
2133 if (pCurrent->msExpire <= msNow && pCurrent->msExpire > 0)
2134 {
2135 pCurrent->msExpire = 0;
2136 pCurrent->pHandler(pCurrent->opaque);
2137 }
2138 pCurrent = pNext;
2139 }
2140}
2141
2142
2143/**
2144 * Reduce the given timeout to match the earliest timer deadline.
2145 *
2146 * @returns Updated cMsTimeout value.
2147 * @param cMsTimeout The timeout to adjust, in milliseconds.
2148 *
2149 * @thread pSlirpThread
2150 */
2151int VBoxNetSlirpNAT::slirpTimersAdjustTimeoutDown(int cMsTimeout)
2152{
2153 /** @todo r=bird: This and a most other stuff would be easier if msExpire was
2154 * unsigned and we used UINT64_MAX for stopped timers. */
2155 /** @todo The timer code isn't thread safe, it assumes a single user thread
2156 * (pSlirpThread). */
2157
2158 /* Find the first (lowest) deadline. */
2159 int64_t msDeadline = INT64_MAX;
2160 for (SlirpTimer *pCurrent = pTimerHead; pCurrent; pCurrent = pCurrent->next)
2161 if (pCurrent->msExpire < msDeadline && pCurrent->msExpire > 0)
2162 msDeadline = pCurrent->msExpire;
2163
2164 /* Adjust the timeout if there is a timer with a deadline. */
2165 if (msDeadline < INT64_MAX)
2166 {
2167 int64_t const msNow = slirpClockGetNsCb(NULL) / RT_NS_1MS;
2168 if (msNow < msDeadline)
2169 {
2170 int64_t cMilliesToDeadline = msDeadline - msNow;
2171 if (cMilliesToDeadline < cMsTimeout)
2172 cMsTimeout = (int)cMilliesToDeadline;
2173 }
2174 else
2175 cMsTimeout = 0;
2176 }
2177
2178 return cMsTimeout;
2179}
2180
2181
2182/**
2183 * Slirp polling thread.
2184 */
2185/* static */ DECLCALLBACK(int)
2186VBoxNetSlirpNAT::pollThread(RTTHREAD hThreadSelf, void *pvUser)
2187{
2188 RT_NOREF(hThreadSelf);
2189
2190 AssertReturn(pvUser != NULL, VERR_INVALID_PARAMETER);
2191 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
2192
2193 /* Activate the initial port forwarding rules. */
2194 pThis->natServiceProcessRegisteredPf(pThis->m_vecPortForwardRule4);
2195 pThis->natServiceProcessRegisteredPf(pThis->m_vecPortForwardRule6);
2196
2197 /* The first polling entry is for the control/wakeup pipe. */
2198#ifdef RT_OS_WINDOWS
2199 pThis->polls[0].fd = pThis->m_ahWakeupSockPair[1];
2200#else
2201 unsigned int cPollNegRet = 0;
2202 RTHCINTPTR const i64NativeReadPipe = RTPipeToNative(pThis->m_hPipeRead);
2203 int const fdNativeReadPipe = (int)i64NativeReadPipe;
2204 Assert(fdNativeReadPipe == i64NativeReadPipe); Assert(fdNativeReadPipe >= 0);
2205 pThis->polls[0].fd = fdNativeReadPipe;
2206 pThis->polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
2207 pThis->polls[0].revents = 0;
2208#endif /* !RT_OS_WINDOWS */
2209
2210 /*
2211 * Polling loop.
2212 */
2213 for (;;)
2214 {
2215 /*
2216 * To prevent concurrent execution of sending/receiving threads
2217 */
2218 pThis->nsock = 1;
2219
2220 uint32_t cMsTimeout = DRVNAT_DEFAULT_TIMEOUT;
2221 slirp_pollfds_fill_socket(pThis->m_pSlirp, &cMsTimeout, pThis->slirpAddPollCb /* SlirpAddPollCb */, pThis /* opaque */);
2222 cMsTimeout = pThis->slirpTimersAdjustTimeoutDown(cMsTimeout);
2223
2224#ifdef RT_OS_WINDOWS
2225 int cChangedFDs = WSAPoll(pThis->polls, pThis->nsock, cMsTimeout);
2226#else
2227 int cChangedFDs = poll(pThis->polls, pThis->nsock, cMsTimeout);
2228#endif
2229 if (cChangedFDs < 0)
2230 {
2231#ifdef RT_OS_WINDOWS
2232 int const iLastErr = WSAGetLastError(); /* (In debug builds LogRel translates to two RTLogLoggerExWeak calls.) */
2233 LogRel(("NAT: RTWinPoll returned error=%Rrc (cChangedFDs=%d)\n", iLastErr, cChangedFDs));
2234 Log4(("NAT: NSOCK = %d\n", pThis->nsock));
2235#else
2236 if (errno == EINTR)
2237 {
2238 Log2(("NAT: signal was caught while sleep on poll\n"));
2239 /* No error, just process all outstanding requests but don't wait */
2240 cChangedFDs = 0;
2241 }
2242 else if (cPollNegRet++ > 128)
2243 {
2244 LogRel(("NAT: Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
2245 cPollNegRet = 0;
2246 }
2247#endif
2248 }
2249
2250 Log4(("%s: poll\n", __FUNCTION__));
2251 slirp_pollfds_poll(pThis->m_pSlirp, cChangedFDs < 0, pThis->slirpGetREventsCb, pThis /* opaque */);
2252
2253 /*
2254 * Drain the control pipe if necessary.
2255 */
2256 if (pThis->polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND)) /* POLLPRI won't be seen with WSAPoll. */
2257 {
2258 char achBuf[1024];
2259 size_t cbRead;
2260 uint64_t cWakeupNotifs = ASMAtomicReadU64(&pThis->m_cWakeupNotifs);
2261#ifdef RT_OS_WINDOWS
2262 /** @todo r=bird: This returns -1 (SOCKET_ERROR) on failure, so any kind of
2263 * error return here and we'll bugger up cbWakeupNotifs! */
2264 cbRead = recv(pThis->m_ahWakeupSockPair[1], &achBuf[0], RT_MIN(cWakeupNotifs, sizeof(achBuf)), NULL);
2265#else
2266 /** @todo r=bird: cbRead may in theory be used uninitialized here! This
2267 * isn't blocking, though, so we won't get stuck here if we mess up
2268 * the count. */
2269 RTPipeRead(pThis->m_hPipeRead, &achBuf[0], RT_MIN(cWakeupNotifs, sizeof(achBuf)), &cbRead);
2270#endif
2271 ASMAtomicSubU64(&pThis->m_cWakeupNotifs, cbRead);
2272 }
2273
2274 /* process _all_ outstanding requests but don't wait */
2275 RTReqQueueProcess(pThis->m_hSlirpReqQueue, 0);
2276 pThis->timersRunExpired();
2277 }
2278
2279#if 0
2280 LogRel(("pollThread: Exiting\n"));
2281 return VERR_INVALID_STATE;
2282#endif
2283}
2284
2285
2286/**
2287 * IntNetIf receive thread. Runs intnet pump with our processFrame()
2288 * as input callback.
2289 */
2290/* static */ DECLCALLBACK(int)
2291VBoxNetSlirpNAT::receiveThread(RTTHREAD hThreadSelf, void *pvUser)
2292{
2293 HRESULT hrc;
2294 int rc;
2295
2296 RT_NOREF(hThreadSelf);
2297
2298 AssertReturn(pvUser != NULL, VERR_INVALID_PARAMETER);
2299 VBoxNetSlirpNAT *self = static_cast<VBoxNetSlirpNAT *>(pvUser);
2300
2301 /* do we relaly need to init com on this thread? */
2302 hrc = com::Initialize();
2303 if (FAILED(hrc))
2304 return VERR_GENERAL_FAILURE;
2305
2306 rc = IntNetR3IfPumpPkts(self->m_hIf, VBoxNetSlirpNAT::processFrame, self,
2307 NULL /*pfnInputGso*/, NULL /*pvUserGso*/);
2308 if (rc == VERR_SEM_DESTROYED)
2309 return VINF_SUCCESS;
2310
2311 LogRel(("receiveThread: IntNetR3IfPumpPkts: unexpected %Rrc\n", rc));
2312 return VERR_INVALID_STATE;
2313}
2314
2315
2316/**
2317 * Worker function for drvNATSend().
2318 *
2319 * @param pThis Pointer to the NAT instance.
2320 * @param pvFrame Pointer to the frame data.
2321 * @param cbFrame Size of the frame in bytes.
2322 * @thread NAT
2323 */
2324/*static*/ DECLCALLBACK(void) VBoxNetSlirpNAT::slirpSendWorker(VBoxNetSlirpNAT *pThis, void *pvFrame, size_t cbFrame)
2325{
2326 LogFlowFunc(("pThis=%p pvFrame=%p cbFrame=%zu\n", pThis, pvFrame, cbFrame));
2327
2328 slirp_input(pThis->m_pSlirp, (uint8_t const *)pvFrame, (int)cbFrame);
2329
2330 LogFlowFunc(("leave\n"));
2331 RTMemFree(pvFrame);
2332}
2333
2334
2335/**
2336 * Process an incoming frame received from the intnet.
2337 */
2338/* static */ DECLCALLBACK(void)
2339VBoxNetSlirpNAT::processFrame(void *pvUser, void *pvFrame, uint32_t cbFrame)
2340{
2341 AssertReturnVoid(pvFrame != NULL);
2342
2343 LogFlowFunc(("processFrame:\n"));
2344
2345 /* shouldn't happen, but if it does, don't even bother */
2346 if (RT_UNLIKELY(cbFrame < sizeof(RTNETETHERHDR)))
2347 return;
2348
2349 /* we expect normal ethernet frame including .1Q and FCS */
2350 if (cbFrame > 1522)
2351 return;
2352
2353 AssertReturnVoid(pvUser != NULL);
2354 VBoxNetSlirpNAT *pThis = static_cast<VBoxNetSlirpNAT *>(pvUser);
2355
2356 const void *pvBuf = RTMemDup(pvFrame, cbFrame);
2357 if (!pvBuf)
2358 return;
2359
2360 int rc = RTReqQueueCallEx(pThis->m_hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
2361 (PFNRT)pThis->slirpSendWorker, 3, pThis, pvBuf, cbFrame);
2362 if (RT_SUCCESS(rc))
2363 {
2364 pThis->slirpNotifyPollThread("processFrame");
2365 LogFlowFunc(("leave success\n"));
2366 }
2367}
2368
2369
2370/**
2371 * Retrieve network-specific extra data item.
2372 */
2373int VBoxNetSlirpNAT::getExtraData(com::Utf8Str &strValueOut, const char *pcszKey)
2374{
2375 HRESULT hrc;
2376
2377 AssertReturn(!virtualbox.isNull(), E_FAIL);
2378 AssertReturn(m_strNetworkName.isNotEmpty(), E_FAIL);
2379 AssertReturn(pcszKey != NULL, E_FAIL);
2380 AssertReturn(*pcszKey != '\0', E_FAIL);
2381
2382 com::BstrFmt bstrKey("NAT/%s/%s", m_strNetworkName.c_str(), pcszKey);
2383 com::Bstr bstrValue;
2384 hrc = virtualbox->GetExtraData(bstrKey.raw(), bstrValue.asOutParam());
2385 if (FAILED(hrc))
2386 {
2387 reportComError(virtualbox, "GetExtraData", hrc);
2388 return VERR_GENERAL_FAILURE;
2389 }
2390
2391 strValueOut = bstrValue;
2392 return VINF_SUCCESS;
2393}
2394
2395
2396/* static */
2397HRESULT VBoxNetSlirpNAT::reportComError(ComPtr<IUnknown> iface,
2398 const com::Utf8Str &strContext,
2399 HRESULT hrc)
2400{
2401 const com::ErrorInfo info(iface, COM_IIDOF(IUnknown));
2402 if (info.isFullAvailable() || info.isBasicAvailable())
2403 {
2404 reportErrorInfoList(info, strContext);
2405 }
2406 else
2407 {
2408 if (strContext.isNotEmpty())
2409 reportError("%s: %Rhra", strContext.c_str(), hrc);
2410 else
2411 reportError("%Rhra", hrc);
2412 }
2413
2414 return hrc;
2415}
2416
2417
2418/* static */
2419void VBoxNetSlirpNAT::reportErrorInfoList(const com::ErrorInfo &info,
2420 const com::Utf8Str &strContext)
2421{
2422 if (strContext.isNotEmpty())
2423 reportError("%s", strContext.c_str());
2424
2425 bool fFirst = true;
2426 for (const com::ErrorInfo *pInfo = &info;
2427 pInfo != NULL;
2428 pInfo = pInfo->getNext())
2429 {
2430 if (fFirst)
2431 fFirst = false;
2432 else
2433 reportError("--------");
2434
2435 reportErrorInfo(*pInfo);
2436 }
2437}
2438
2439
2440/* static */
2441void VBoxNetSlirpNAT::reportErrorInfo(const com::ErrorInfo &info)
2442{
2443#if defined (RT_OS_WIN)
2444 bool haveResultCode = info.isFullAvailable();
2445 bool haveComponent = true;
2446 bool haveInterfaceID = true;
2447#else /* !RT_OS_WIN */
2448 bool haveResultCode = true;
2449 bool haveComponent = info.isFullAvailable();
2450 bool haveInterfaceID = info.isFullAvailable();
2451#endif
2452 com::Utf8Str message;
2453 if (info.getText().isNotEmpty())
2454 message = info.getText();
2455
2456 const char *pcszDetails = "Details: ";
2457 const char *pcszComma = ", ";
2458 const char *pcszSeparator = pcszDetails;
2459
2460 if (haveResultCode)
2461 {
2462 message.appendPrintf("%s" "code %Rhrc (0x%RX32)",
2463 pcszSeparator, info.getResultCode(), info.getResultCode());
2464 pcszSeparator = pcszComma;
2465 }
2466
2467 if (haveComponent)
2468 {
2469 message.appendPrintf("%s" "component %ls",
2470 pcszSeparator, info.getComponent().raw());
2471 pcszSeparator = pcszComma;
2472 }
2473
2474 if (haveInterfaceID)
2475 {
2476 message.appendPrintf("%s" "interface %ls",
2477 pcszSeparator, info.getInterfaceName().raw());
2478 pcszSeparator = pcszComma;
2479 }
2480
2481 if (info.getCalleeName().isNotEmpty())
2482 {
2483 message.appendPrintf("%s" "callee %ls",
2484 pcszSeparator, info.getCalleeName().raw());
2485 //pcszSeparator = pcszComma; unused
2486 }
2487
2488 reportError("%s", message.c_str());
2489}
2490
2491
2492/* static */
2493void VBoxNetSlirpNAT::reportError(const char *a_pcszFormat, ...)
2494{
2495 va_list ap;
2496
2497 va_start(ap, a_pcszFormat);
2498 com::Utf8Str message(a_pcszFormat, ap);
2499 va_end(ap);
2500
2501 RTMsgError("%s", message.c_str());
2502 LogRel(("%s", message.c_str()));
2503}
2504
2505
2506
2507/**
2508 * Create release logger.
2509 *
2510 * The NAT network name is sanitized so that it can be used in a path
2511 * component. By default the release log is written to the file
2512 * ~/.VirtualBox/${netname}.log but its destiation and content can be
2513 * overridden with VBOXNET_${netname}_RELEASE_LOG family of
2514 * environment variables (also ..._DEST and ..._FLAGS).
2515 */
2516/* static */
2517int VBoxNetSlirpNAT::initLog()
2518{
2519 size_t cch;
2520 int rc;
2521
2522 if (m_strNetworkName.isEmpty())
2523 return VERR_MISSING;
2524
2525 char szNetwork[RTPATH_MAX];
2526 rc = RTStrCopy(szNetwork, sizeof(szNetwork), m_strNetworkName.c_str());
2527 if (RT_FAILURE(rc))
2528 return rc;
2529
2530 // sanitize network name to be usable as a path component
2531 for (char *p = szNetwork; *p != '\0'; ++p)
2532 {
2533 if (RTPATH_IS_SEP(*p))
2534 *p = '_';
2535 }
2536
2537 const char *pcszLogFile = NULL;
2538 char szLogFile[RTPATH_MAX];
2539 if (m_strHome.isNotEmpty())
2540 {
2541 cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
2542 "%s%c%s.log", m_strHome.c_str(), RTPATH_DELIMITER, szNetwork);
2543 if (cch < sizeof(szLogFile))
2544 pcszLogFile = szLogFile;
2545 }
2546
2547 // sanitize network name some more to be usable as environment variable
2548 for (char *p = szNetwork; *p != '\0'; ++p)
2549 {
2550 if (*p != '_'
2551 && (*p < '0' || '9' < *p)
2552 && (*p < 'a' || 'z' < *p)
2553 && (*p < 'A' || 'Z' < *p))
2554 {
2555 *p = '_';
2556 }
2557 }
2558
2559 char szEnvVarBase[128];
2560 const char *pcszEnvVarBase = szEnvVarBase;
2561 cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
2562 "VBOXNET_%s_RELEASE_LOG", szNetwork);
2563 if (cch >= sizeof(szEnvVarBase))
2564 pcszEnvVarBase = NULL;
2565
2566 rc = com::VBoxLogRelCreate("NAT Network",
2567 pcszLogFile,
2568 RTLOGFLAGS_PREFIX_TIME_PROG,
2569 "all all.restrict -default.restrict",
2570 pcszEnvVarBase,
2571 RTLOGDEST_FILE,
2572 32768 /* cMaxEntriesPerGroup */,
2573 0 /* cHistory */,
2574 0 /* uHistoryFileTime */,
2575 0 /* uHistoryFileSize */,
2576 NULL /*pErrInfo*/);
2577
2578 /*
2579 * Provide immediate feedback if corresponding LogRel level is
2580 * enabled. It's frustrating when you chase some rare event and
2581 * discover you didn't actually have the corresponding log level
2582 * enabled because of a typo in the environment variable name or
2583 * its content.
2584 */
2585#define LOG_PING(_log) _log((#_log " enabled\n"))
2586 LOG_PING(LogRel2);
2587 LOG_PING(LogRel3);
2588 LOG_PING(LogRel4);
2589 LOG_PING(LogRel5);
2590 LOG_PING(LogRel6);
2591 LOG_PING(LogRel7);
2592 LOG_PING(LogRel8);
2593 LOG_PING(LogRel9);
2594 LOG_PING(LogRel10);
2595 LOG_PING(LogRel11);
2596 LOG_PING(LogRel12);
2597
2598 return rc;
2599}
2600
2601
2602/**
2603 * Entry point.
2604 */
2605extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
2606{
2607 LogFlowFuncEnter();
2608 NOREF(envp);
2609
2610#ifdef RT_OS_WINDOWS
2611 WSADATA WsaData = {0};
2612 int err = WSAStartup(MAKEWORD(2,2), &WsaData);
2613 if (err)
2614 {
2615 fprintf(stderr, "wsastartup: failed (%d)\n", err);
2616 return RTEXITCODE_INIT;
2617 }
2618#endif
2619
2620 VBoxNetSlirpNAT NAT;
2621
2622 int rcExit = NAT.parseArgs(argc, argv);
2623 if (rcExit != RTEXITCODE_SUCCESS)
2624 {
2625 /* messages are already printed */
2626 return rcExit == RTEXITCODE_DONE ? RTEXITCODE_SUCCESS : rcExit;
2627 }
2628
2629 int rc = NAT.init();
2630 if (RT_FAILURE(rc))
2631 return RTEXITCODE_INIT;
2632
2633 NAT.run();
2634
2635 LogRel(("Terminating\n"));
2636 return RTEXITCODE_SUCCESS;
2637}
2638
2639
2640#ifndef VBOX_WITH_HARDENING
2641
2642int main(int argc, char **argv, char **envp)
2643{
2644 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
2645 if (RT_SUCCESS(rc))
2646 return TrustedMain(argc, argv, envp);
2647 return RTMsgInitFailure(rc);
2648}
2649
2650# if defined(RT_OS_WINDOWS)
2651
2652/** (We don't want a console usually.) */
2653int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2654{
2655 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
2656 return main(__argc, __argv, environ);
2657}
2658# endif /* RT_OS_WINDOWS */
2659
2660#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