VirtualBox

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

Last change on this file since 109095 was 109095, checked in by vboxsync, 13 days ago

NetworkServices/NAT: Some early sketch for NAT proxy based on libslirp instead of our beefed up lwip variant, bugref:10268

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