VirtualBox

source: vbox/trunk/src/VBox/NetworkServices/NAT/VBoxNetLwipNAT.cpp@ 97071

Last change on this file since 97071 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.3 KB
Line 
1/* $Id: VBoxNetLwipNAT.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VBoxNetNAT - NAT Service for connecting to IntNet.
4 */
5
6/*
7 * Copyright (C) 2009-2022 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
97extern "C"
98{
99/* bunch of LWIP headers */
100#include "lwip/sys.h"
101#include "lwip/pbuf.h"
102#include "lwip/netif.h"
103#include "lwip/ethip6.h"
104#include "lwip/nd6.h" // for proxy_na_hook
105#include "lwip/mld6.h"
106#include "lwip/tcpip.h"
107#include "netif/etharp.h"
108
109#include "proxy.h"
110#include "pxremap.h"
111#include "portfwd.h"
112}
113
114#include "VBoxLwipCore.h"
115
116#ifdef VBOX_RAWSOCK_DEBUG_HELPER
117#if defined(VBOX_WITH_HARDENING) /* obviously */ \
118 || defined(RT_OS_WINDOWS) /* not used */ \
119 || defined(RT_OS_DARWIN) /* not necessary */
120# error Have you forgotten to turn off VBOX_RAWSOCK_DEBUG_HELPER?
121#endif
122/* ask the privileged helper to create a raw socket for us */
123extern "C" int getrawsock(int type);
124#endif
125
126
127
128typedef struct NATSERVICEPORTFORWARDRULE
129{
130 PORTFORWARDRULE Pfr;
131 fwspec FWSpec;
132} NATSERVICEPORTFORWARDRULE, *PNATSERVICEPORTFORWARDRULE;
133
134typedef std::vector<NATSERVICEPORTFORWARDRULE> VECNATSERVICEPF;
135typedef VECNATSERVICEPF::iterator ITERATORNATSERVICEPF;
136typedef VECNATSERVICEPF::const_iterator CITERATORNATSERVICEPF;
137
138
139class VBoxNetLwipNAT
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 IntNetIf m_IntNetIf;
153 RTTHREAD m_hThrRecv;
154
155 /** Home folder location; used as default directory for several paths. */
156 com::Utf8Str m_strHome;
157
158 struct proxy_options m_ProxyOptions;
159 struct sockaddr_in m_src4;
160 struct sockaddr_in6 m_src6;
161 /**
162 * place for registered local interfaces.
163 */
164 ip4_lomap m_lo2off[10];
165 ip4_lomap_desc m_loOptDescriptor;
166
167 uint16_t m_u16Mtu;
168 netif m_LwipNetIf;
169
170 VECNATSERVICEPF m_vecPortForwardRule4;
171 VECNATSERVICEPF m_vecPortForwardRule6;
172
173 class Listener
174 {
175 class Adapter;
176 typedef ListenerImpl<Adapter, VBoxNetLwipNAT *> Impl;
177
178 ComObjPtr<Impl> m_pListenerImpl;
179 ComPtr<IEventSource> m_pEventSource;
180
181 public:
182 HRESULT init(VBoxNetLwipNAT *pNAT);
183 void uninit();
184
185 template <typename IEventful>
186 HRESULT listen(const ComPtr<IEventful> &pEventful,
187 const VBoxEventType_T aEvents[]);
188 HRESULT unlisten();
189
190 private:
191 HRESULT doListen(const ComPtr<IEventSource> &pEventSource,
192 const VBoxEventType_T aEvents[]);
193 };
194
195 Listener m_ListenerNATNet;
196 Listener m_ListenerVirtualBox;
197 Listener m_ListenerVBoxClient;
198
199public:
200 VBoxNetLwipNAT();
201 ~VBoxNetLwipNAT();
202
203 RTEXITCODE parseArgs(int argc, char *argv[]);
204
205 int init();
206 int run();
207 void shutdown();
208
209private:
210 RTEXITCODE usage();
211
212 int initCom();
213 int initHome();
214 int initLog();
215 int initIPv4();
216 int initIPv4LoopbackMap();
217 int initIPv6();
218 int initComEvents();
219
220 int getExtraData(com::Utf8Str &strValueOut, const char *pcszKey);
221
222 static void reportError(const char *a_pcszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
223
224 static HRESULT reportComError(ComPtr<IUnknown> iface,
225 const com::Utf8Str &strContext,
226 HRESULT hrc);
227 static void reportErrorInfoList(const com::ErrorInfo &info,
228 const com::Utf8Str &strContext);
229 static void reportErrorInfo(const com::ErrorInfo &info);
230
231 void initIPv4RawSock();
232 void initIPv6RawSock();
233
234 static DECLCALLBACK(void) onLwipTcpIpInit(void *arg);
235 static DECLCALLBACK(void) onLwipTcpIpFini(void *arg);
236 static DECLCALLBACK(err_t) netifInit(netif *pNetif) RT_NOTHROW_PROTO;
237
238 HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent);
239
240 const char **getHostNameservers();
241
242 int fetchNatPortForwardRules(VECNATSERVICEPF &vec, bool fIsIPv6);
243 static int natServiceProcessRegisteredPf(VECNATSERVICEPF &vecPf);
244 static int natServicePfRegister(NATSERVICEPORTFORWARDRULE &natServicePf);
245
246 static DECLCALLBACK(int) receiveThread(RTTHREAD hThreadSelf, void *pvUser);
247
248 /* input from intnet */
249 static DECLCALLBACK(void) processFrame(void *pvUser, void *pvFrame, uint32_t cbFrame);
250
251 /* output to intnet */
252 static DECLCALLBACK(err_t) netifLinkoutput(netif *pNetif, pbuf *pBuf) RT_NOTHROW_PROTO;
253};
254
255
256
257VBoxNetLwipNAT::VBoxNetLwipNAT()
258 : m_uVerbosity(0),
259 m_hThrRecv(NIL_RTTHREAD)
260{
261 LogFlowFuncEnter();
262
263 RT_ZERO(m_ProxyOptions.ipv4_addr);
264 RT_ZERO(m_ProxyOptions.ipv4_mask);
265 RT_ZERO(m_ProxyOptions.ipv6_addr);
266 m_ProxyOptions.ipv6_enabled = 0;
267 m_ProxyOptions.ipv6_defroute = 0;
268 m_ProxyOptions.icmpsock4 = INVALID_SOCKET;
269 m_ProxyOptions.icmpsock6 = INVALID_SOCKET;
270 m_ProxyOptions.tftp_root = NULL;
271 m_ProxyOptions.src4 = NULL;
272 m_ProxyOptions.src6 = NULL;
273 RT_ZERO(m_src4);
274 RT_ZERO(m_src6);
275 m_src4.sin_family = AF_INET;
276 m_src6.sin6_family = AF_INET6;
277#if HAVE_SA_LEN
278 m_src4.sin_len = sizeof(m_src4);
279 m_src6.sin6_len = sizeof(m_src6);
280#endif
281 m_ProxyOptions.lomap_desc = NULL;
282 m_ProxyOptions.nameservers = NULL;
283
284 m_LwipNetIf.name[0] = 'N';
285 m_LwipNetIf.name[1] = 'T';
286
287 m_MacAddress.au8[0] = 0x52;
288 m_MacAddress.au8[1] = 0x54;
289 m_MacAddress.au8[2] = 0;
290 m_MacAddress.au8[3] = 0x12;
291 m_MacAddress.au8[4] = 0x35;
292 m_MacAddress.au8[5] = 0;
293
294 RT_ZERO(m_lo2off);
295 m_loOptDescriptor.lomap = NULL;
296 m_loOptDescriptor.num_lomap = 0;
297
298 LogFlowFuncLeave();
299}
300
301
302VBoxNetLwipNAT::~VBoxNetLwipNAT()
303{
304 if (m_ProxyOptions.tftp_root)
305 {
306 RTStrFree((char *)m_ProxyOptions.tftp_root);
307 m_ProxyOptions.tftp_root = NULL;
308 }
309 if (m_ProxyOptions.nameservers)
310 {
311 const char **pv = m_ProxyOptions.nameservers;
312 while (*pv)
313 {
314 RTStrFree((char*)*pv);
315 pv++;
316 }
317 RTMemFree(m_ProxyOptions.nameservers);
318 m_ProxyOptions.nameservers = NULL;
319 }
320}
321
322
323/**
324 * Command line options.
325 */
326RTGETOPTDEF VBoxNetLwipNAT::s_aGetOptDef[] =
327{
328 { "--network", 'n', RTGETOPT_REQ_STRING },
329 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
330};
331
332
333/** Icky hack to tell the caller it should exit with RTEXITCODE_SUCCESS */
334#define RTEXITCODE_DONE RTEXITCODE_32BIT_HACK
335
336RTEXITCODE
337VBoxNetLwipNAT::usage()
338{
339 RTPrintf("%s Version %sr%u\n"
340 "Copyright (C) 2009-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
341 "\n"
342 "Usage: %s <options>\n"
343 "\n"
344 "Options:\n",
345 RTProcShortName(), RTBldCfgVersion(), RTBldCfgRevision(),
346 RTProcShortName());
347 for (size_t i = 0; i < RT_ELEMENTS(s_aGetOptDef); ++i)
348 RTPrintf(" -%c, %s\n", s_aGetOptDef[i].iShort, s_aGetOptDef[i].pszLong);
349
350 return RTEXITCODE_DONE;
351}
352
353
354RTEXITCODE
355VBoxNetLwipNAT::parseArgs(int argc, char *argv[])
356{
357 unsigned int uVerbosity = 0;
358 int rc;
359
360 RTGETOPTSTATE State;
361 rc = RTGetOptInit(&State, argc, argv,
362 s_aGetOptDef, RT_ELEMENTS(s_aGetOptDef),
363 1, 0);
364
365 int ch;
366 RTGETOPTUNION Val;
367 while ((ch = RTGetOpt(&State, &Val)) != 0)
368 {
369 switch (ch)
370 {
371 case 'n': /* --network */
372 if (m_strNetworkName.isNotEmpty())
373 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "multiple --network options");
374 m_strNetworkName = Val.psz;
375 break;
376
377 case 'v': /* --verbose */
378 ++uVerbosity;
379 break;
380
381
382 /*
383 * Standard options recognized by RTGetOpt()
384 */
385
386 case 'V': /* --version */
387 RTPrintf("%sr%u\n", RTBldCfgVersion(), RTBldCfgRevision());
388 return RTEXITCODE_DONE;
389
390 case 'h': /* --help */
391 return usage();
392
393 case VINF_GETOPT_NOT_OPTION:
394 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "unexpected non-option argument");
395
396 default:
397 return RTGetOptPrintError(ch, &Val);
398 }
399 }
400
401 if (m_strNetworkName.isEmpty())
402 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "missing --network option");
403
404 m_uVerbosity = uVerbosity;
405 return RTEXITCODE_SUCCESS;
406}
407
408
409/**
410 * Perform actual initialization.
411 *
412 * This code runs on the main thread. Establish COM connection with
413 * VBoxSVC so that we can do API calls. Starts the LWIP thread.
414 */
415int VBoxNetLwipNAT::init()
416{
417 HRESULT hrc;
418 int rc;
419
420 LogFlowFuncEnter();
421
422 /* Get the COM API set up. */
423 rc = initCom();
424 if (RT_FAILURE(rc))
425 return rc;
426
427 /* Get the home folder location. It's ok if it fails. */
428 initHome();
429
430 /*
431 * We get the network name on the command line. Get hold of its
432 * API object to get the rest of the configuration from.
433 */
434 hrc = virtualbox->FindNATNetworkByName(com::Bstr(m_strNetworkName).raw(),
435 m_net.asOutParam());
436 if (FAILED(hrc))
437 {
438 reportComError(virtualbox, "FindNATNetworkByName", hrc);
439 return VERR_NOT_FOUND;
440 }
441
442 /*
443 * Now that we know the network name and have ensured that it
444 * indeed exists we can create the release log file.
445 */
446 initLog();
447
448 // resolver changes are reported on vbox but are retrieved from
449 // host so stash a pointer for future lookups
450 hrc = virtualbox->COMGETTER(Host)(m_host.asOutParam());
451 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
452
453
454 /* Get the settings related to IPv4. */
455 rc = initIPv4();
456 if (RT_FAILURE(rc))
457 return rc;
458
459 /* Get the settings related to IPv6. */
460 rc = initIPv6();
461 if (RT_FAILURE(rc))
462 return rc;
463
464
465 fetchNatPortForwardRules(m_vecPortForwardRule4, /* :fIsIPv6 */ false);
466 if (m_ProxyOptions.ipv6_enabled)
467 fetchNatPortForwardRules(m_vecPortForwardRule6, /* :fIsIPv6 */ true);
468
469
470 if (m_strHome.isNotEmpty())
471 {
472 com::Utf8StrFmt strTftpRoot("%s%c%s", m_strHome.c_str(), RTPATH_DELIMITER, "TFTP");
473 char *pszStrTemp; // avoid const char ** vs char **
474 rc = RTStrUtf8ToCurrentCP(&pszStrTemp, strTftpRoot.c_str());
475 AssertRC(rc);
476 m_ProxyOptions.tftp_root = pszStrTemp;
477 }
478
479 m_ProxyOptions.nameservers = getHostNameservers();
480
481 initComEvents();
482 /* end of COM initialization */
483
484 /* connect to the intnet */
485 rc = m_IntNetIf.init(m_strNetworkName);
486 if (RT_FAILURE(rc))
487 return rc;
488
489 LogFlowFuncLeaveRC(rc);
490 return rc;
491}
492
493
494/**
495 * Primary COM initialization performed on the main thread.
496 *
497 * This initializes COM and obtains VirtualBox Client and VirtualBox
498 * objects.
499 *
500 * @note The member variables for them are in the base class. We
501 * currently do it here so that we can report errors properly, because
502 * the base class' VBoxNetBaseService::init() is a bit naive and
503 * fixing that would just create unnecessary churn for little
504 * immediate gain. It's easier to ignore the base class code and do
505 * it ourselves and do the refactoring later.
506 */
507int VBoxNetLwipNAT::initCom()
508{
509 HRESULT hrc;
510
511 hrc = com::Initialize();
512 if (FAILED(hrc))
513 {
514#ifdef VBOX_WITH_XPCOM
515 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
516 {
517 char szHome[RTPATH_MAX] = "";
518 int vrc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
519 if (RT_SUCCESS(vrc))
520 {
521 return RTMsgErrorExit(RTEXITCODE_INIT,
522 "Failed to initialize COM: %s: %Rhrf",
523 szHome, hrc);
524 }
525 }
526#endif /* VBOX_WITH_XPCOM */
527 return RTMsgErrorExit(RTEXITCODE_INIT,
528 "Failed to initialize COM: %Rhrf", hrc);
529 }
530
531 hrc = virtualboxClient.createInprocObject(CLSID_VirtualBoxClient);
532 if (FAILED(hrc))
533 {
534 reportError("Failed to create VirtualBox Client object: %Rhra", hrc);
535 return VERR_GENERAL_FAILURE;
536 }
537
538 hrc = virtualboxClient->COMGETTER(VirtualBox)(virtualbox.asOutParam());
539 if (FAILED(hrc))
540 {
541 reportError("Failed to obtain VirtualBox object: %Rhra", hrc);
542 return VERR_GENERAL_FAILURE;
543 }
544
545 return VINF_SUCCESS;
546}
547
548
549/**
550 * Get the VirtualBox home folder.
551 *
552 * It is used as the base directory for the default release log file
553 * and for the TFTP root location.
554 */
555int VBoxNetLwipNAT::initHome()
556{
557 HRESULT hrc;
558 int rc;
559
560 com::Bstr bstrHome;
561 hrc = virtualbox->COMGETTER(HomeFolder)(bstrHome.asOutParam());
562 if (SUCCEEDED(hrc))
563 {
564 m_strHome = bstrHome;
565 return VINF_SUCCESS;
566 }
567
568 /*
569 * In the unlikely event that we have failed to retrieve
570 * HomeFolder via the API, try the fallback method. Note that
571 * despite "com" namespace it does not use COM.
572 */
573 char szHome[RTPATH_MAX] = "";
574 rc = com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome), false);
575 if (RT_SUCCESS(rc))
576 {
577 m_strHome = szHome;
578 return VINF_SUCCESS;
579 }
580
581 return rc;
582}
583
584
585/*
586 * Read IPv4 related settings and do necessary initialization. These
587 * settings will be picked up by the proxy on the lwIP thread. See
588 * onLwipTcpIpInit().
589 */
590int VBoxNetLwipNAT::initIPv4()
591{
592 HRESULT hrc;
593 int rc;
594
595 AssertReturn(m_net.isNotNull(), VERR_GENERAL_FAILURE);
596
597
598 /*
599 * IPv4 address and mask.
600 */
601 com::Bstr bstrIPv4Prefix;
602 hrc = m_net->COMGETTER(Network)(bstrIPv4Prefix.asOutParam());
603 if (FAILED(hrc))
604 {
605 reportComError(m_net, "Network", hrc);
606 return VERR_GENERAL_FAILURE;
607 }
608
609 RTNETADDRIPV4 Net4, Mask4;
610 int iPrefixLength;
611 rc = RTNetStrToIPv4Cidr(com::Utf8Str(bstrIPv4Prefix).c_str(),
612 &Net4, &iPrefixLength);
613 if (RT_FAILURE(rc))
614 {
615 reportError("Failed to parse IPv4 prefix %ls\n", bstrIPv4Prefix.raw());
616 return rc;
617 }
618
619 if (iPrefixLength > 30 || 0 >= iPrefixLength)
620 {
621 reportError("Invalid IPv4 prefix length %d\n", iPrefixLength);
622 return VERR_INVALID_PARAMETER;
623 }
624
625 rc = RTNetPrefixToMaskIPv4(iPrefixLength, &Mask4);
626 AssertRCReturn(rc, rc);
627
628 /** @todo r=uwe Check the address is unicast, not a loopback, etc. */
629
630 RTNETADDRIPV4 Addr4;
631 Addr4.u = Net4.u | RT_H2N_U32_C(0x00000001);
632
633 memcpy(&m_ProxyOptions.ipv4_addr, &Addr4, sizeof(ip_addr));
634 memcpy(&m_ProxyOptions.ipv4_mask, &Mask4, sizeof(ip_addr));
635
636
637 /* Raw socket for ICMP. */
638 initIPv4RawSock();
639
640
641 /* IPv4 source address (host), if configured. */
642 com::Utf8Str strSourceIp4;
643 rc = getExtraData(strSourceIp4, "SourceIp4");
644 if (RT_SUCCESS(rc) && strSourceIp4.isNotEmpty())
645 {
646 RTNETADDRIPV4 addr;
647 rc = RTNetStrToIPv4Addr(strSourceIp4.c_str(), &addr);
648 if (RT_SUCCESS(rc))
649 {
650 m_src4.sin_addr.s_addr = addr.u;
651 m_ProxyOptions.src4 = &m_src4;
652
653 LogRel(("Will use %RTnaipv4 as IPv4 source address\n",
654 m_src4.sin_addr.s_addr));
655 }
656 else
657 {
658 LogRel(("Failed to parse \"%s\" IPv4 source address specification\n",
659 strSourceIp4.c_str()));
660 }
661 }
662
663 /* Make host's loopback(s) available from inside the natnet */
664 initIPv4LoopbackMap();
665
666 return VINF_SUCCESS;
667}
668
669
670/**
671 * Create raw IPv4 socket for sending and snooping ICMP.
672 */
673void VBoxNetLwipNAT::initIPv4RawSock()
674{
675 SOCKET icmpsock4 = INVALID_SOCKET;
676
677#ifndef RT_OS_DARWIN
678 const int icmpstype = SOCK_RAW;
679#else
680 /* on OS X it's not privileged */
681 const int icmpstype = SOCK_DGRAM;
682#endif
683
684 icmpsock4 = socket(AF_INET, icmpstype, IPPROTO_ICMP);
685 if (icmpsock4 == INVALID_SOCKET)
686 {
687 perror("IPPROTO_ICMP");
688#ifdef VBOX_RAWSOCK_DEBUG_HELPER
689 icmpsock4 = getrawsock(AF_INET);
690#endif
691 }
692
693 if (icmpsock4 != INVALID_SOCKET)
694 {
695#ifdef ICMP_FILTER // Linux specific
696 struct icmp_filter flt = {
697 ~(uint32_t)(
698 (1U << ICMP_ECHOREPLY)
699 | (1U << ICMP_DEST_UNREACH)
700 | (1U << ICMP_TIME_EXCEEDED)
701 )
702 };
703
704 int status = setsockopt(icmpsock4, SOL_RAW, ICMP_FILTER,
705 &flt, sizeof(flt));
706 if (status < 0)
707 {
708 perror("ICMP_FILTER");
709 }
710#endif
711 }
712
713 m_ProxyOptions.icmpsock4 = icmpsock4;
714}
715
716
717/**
718 * Init mapping from the natnet's IPv4 addresses to host's IPv4
719 * loopbacks. Plural "loopbacks" because it's now quite common to run
720 * services on loopback addresses other than 127.0.0.1. E.g. a
721 * caching dns proxy on 127.0.1.1 or 127.0.0.53.
722 */
723int VBoxNetLwipNAT::initIPv4LoopbackMap()
724{
725 HRESULT hrc;
726 int rc;
727
728 com::SafeArray<BSTR> aStrLocalMappings;
729 hrc = m_net->COMGETTER(LocalMappings)(ComSafeArrayAsOutParam(aStrLocalMappings));
730 if (FAILED(hrc))
731 {
732 reportComError(m_net, "LocalMappings", hrc);
733 return VERR_GENERAL_FAILURE;
734 }
735
736 if (aStrLocalMappings.size() == 0)
737 return VINF_SUCCESS;
738
739
740 /* netmask in host order, to verify the offsets */
741 uint32_t uMask = RT_N2H_U32(ip4_addr_get_u32(&m_ProxyOptions.ipv4_mask));
742
743
744 /*
745 * Process mappings of the form "127.x.y.z=off"
746 */
747 unsigned int dst = 0; /* typeof(ip4_lomap_desc::num_lomap) */
748 for (size_t i = 0; i < aStrLocalMappings.size(); ++i)
749 {
750 com::Utf8Str strMapping(aStrLocalMappings[i]);
751 const char *pcszRule = strMapping.c_str();
752 LogRel(("IPv4 loopback mapping %zu: %s\n", i, pcszRule));
753
754 RTNETADDRIPV4 Loopback4;
755 char *pszNext;
756 rc = RTNetStrToIPv4AddrEx(pcszRule, &Loopback4, &pszNext);
757 if (RT_FAILURE(rc))
758 {
759 LogRel(("Failed to parse IPv4 address: %Rra\n", rc));
760 continue;
761 }
762
763 if (Loopback4.au8[0] != 127)
764 {
765 LogRel(("Not an IPv4 loopback address\n"));
766 continue;
767 }
768
769 if (rc != VWRN_TRAILING_CHARS)
770 {
771 LogRel(("Missing right hand side\n"));
772 continue;
773 }
774
775 pcszRule = RTStrStripL(pszNext);
776 if (*pcszRule != '=')
777 {
778 LogRel(("Invalid rule format\n"));
779 continue;
780 }
781
782 pcszRule = RTStrStripL(pcszRule+1);
783 if (*pszNext == '\0')
784 {
785 LogRel(("Empty right hand side\n"));
786 continue;
787 }
788
789 uint32_t u32Offset;
790 rc = RTStrToUInt32Ex(pcszRule, &pszNext, 10, &u32Offset);
791 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
792 {
793 LogRel(("Invalid offset\n"));
794 continue;
795 }
796
797 if (u32Offset <= 1 || u32Offset == ~uMask)
798 {
799 LogRel(("Offset maps to a reserved address\n"));
800 continue;
801 }
802
803 if ((u32Offset & uMask) != 0)
804 {
805 LogRel(("Offset exceeds the network size\n"));
806 continue;
807 }
808
809 if (dst >= RT_ELEMENTS(m_lo2off))
810 {
811 LogRel(("Ignoring the mapping, too many mappings already\n"));
812 continue;
813 }
814
815 ip4_addr_set_u32(&m_lo2off[dst].loaddr, Loopback4.u);
816 m_lo2off[dst].off = u32Offset;
817 ++dst;
818 }
819
820 if (dst > 0)
821 {
822 m_loOptDescriptor.lomap = m_lo2off;
823 m_loOptDescriptor.num_lomap = dst;
824 m_ProxyOptions.lomap_desc = &m_loOptDescriptor;
825 }
826
827 return VINF_SUCCESS;
828}
829
830
831/*
832 * Read IPv6 related settings and do necessary initialization. These
833 * settings will be picked up by the proxy on the lwIP thread. See
834 * onLwipTcpIpInit().
835 */
836int VBoxNetLwipNAT::initIPv6()
837{
838 HRESULT hrc;
839 int rc;
840
841 AssertReturn(m_net.isNotNull(), VERR_GENERAL_FAILURE);
842
843
844 /* Is IPv6 enabled for this network at all? */
845 BOOL fIPv6Enabled = FALSE;
846 hrc = m_net->COMGETTER(IPv6Enabled)(&fIPv6Enabled);
847 if (FAILED(hrc))
848 {
849 reportComError(m_net, "IPv6Enabled", hrc);
850 return VERR_GENERAL_FAILURE;
851 }
852
853 m_ProxyOptions.ipv6_enabled = !!fIPv6Enabled;
854 if (!fIPv6Enabled)
855 return VINF_SUCCESS;
856
857
858 /*
859 * IPv6 address.
860 */
861 com::Bstr bstrIPv6Prefix;
862 hrc = m_net->COMGETTER(IPv6Prefix)(bstrIPv6Prefix.asOutParam());
863 if (FAILED(hrc))
864 {
865 reportComError(m_net, "IPv6Prefix", hrc);
866 return VERR_GENERAL_FAILURE;
867 }
868
869 RTNETADDRIPV6 Net6;
870 int iPrefixLength;
871 rc = RTNetStrToIPv6Cidr(com::Utf8Str(bstrIPv6Prefix).c_str(),
872 &Net6, &iPrefixLength);
873 if (RT_FAILURE(rc))
874 {
875 reportError("Failed to parse IPv6 prefix %ls\n", bstrIPv6Prefix.raw());
876 return rc;
877 }
878
879 /* Allow both addr:: and addr::/64 */
880 if (iPrefixLength == 128) /* no length was specified after the address? */
881 iPrefixLength = 64; /* take it to mean /64 which we require anyway */
882 else if (iPrefixLength != 64)
883 {
884 reportError("Invalid IPv6 prefix length %d,"
885 " must be 64.\n", iPrefixLength);
886 return rc;
887 }
888
889 /* Verify the address is unicast. */
890 if ( ((Net6.au8[0] & 0xe0) != 0x20) /* global 2000::/3 */
891 && ((Net6.au8[0] & 0xfe) != 0xfc)) /* local fc00::/7 */
892 {
893 reportError("IPv6 prefix %RTnaipv6 is not unicast.\n", &Net6);
894 return VERR_INVALID_PARAMETER;
895 }
896
897 /* Verify the interfaces ID part is zero */
898 if (Net6.au64[1] != 0)
899 {
900 reportError("Non-zero bits in the interface ID part"
901 " of the IPv6 prefix %RTnaipv6/64.\n", &Net6);
902 return VERR_INVALID_PARAMETER;
903 }
904
905 /* Use ...::1 as our address */
906 RTNETADDRIPV6 Addr6 = Net6;
907 Addr6.au8[15] = 0x01;
908 memcpy(&m_ProxyOptions.ipv6_addr, &Addr6, sizeof(ip6_addr_t));
909
910
911 /*
912 * Should we advertise ourselves as default IPv6 route? If the
913 * host doesn't have IPv6 connectivity, it's probably better not
914 * to, to prevent the guest from IPv6 connection attempts doomed
915 * to fail.
916 *
917 * We might want to make this modifiable while the natnet is
918 * running.
919 */
920 BOOL fIPv6DefaultRoute = FALSE;
921 hrc = m_net->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
922 if (FAILED(hrc))
923 {
924 reportComError(m_net, "AdvertiseDefaultIPv6RouteEnabled", hrc);
925 return VERR_GENERAL_FAILURE;
926 }
927
928 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
929
930
931 /* Raw socket for ICMP. */
932 initIPv6RawSock();
933
934
935 /* IPv6 source address, if configured. */
936 com::Utf8Str strSourceIp6;
937 rc = getExtraData(strSourceIp6, "SourceIp6");
938 if (RT_SUCCESS(rc) && strSourceIp6.isNotEmpty())
939 {
940 RTNETADDRIPV6 addr;
941 char *pszZone = NULL;
942 rc = RTNetStrToIPv6Addr(strSourceIp6.c_str(), &addr, &pszZone);
943 if (RT_SUCCESS(rc))
944 {
945 memcpy(&m_src6.sin6_addr, &addr, sizeof(addr));
946 m_ProxyOptions.src6 = &m_src6;
947
948 LogRel(("Will use %RTnaipv6 as IPv6 source address\n",
949 &m_src6.sin6_addr));
950 }
951 else
952 {
953 LogRel(("Failed to parse \"%s\" IPv6 source address specification\n",
954 strSourceIp6.c_str()));
955 }
956 }
957
958 return VINF_SUCCESS;
959}
960
961
962/**
963 * Create raw IPv6 socket for sending and snooping ICMP6.
964 */
965void VBoxNetLwipNAT::initIPv6RawSock()
966{
967 SOCKET icmpsock6 = INVALID_SOCKET;
968
969#ifndef RT_OS_DARWIN
970 const int icmpstype = SOCK_RAW;
971#else
972 /* on OS X it's not privileged */
973 const int icmpstype = SOCK_DGRAM;
974#endif
975
976 icmpsock6 = socket(AF_INET6, icmpstype, IPPROTO_ICMPV6);
977 if (icmpsock6 == INVALID_SOCKET)
978 {
979 perror("IPPROTO_ICMPV6");
980#ifdef VBOX_RAWSOCK_DEBUG_HELPER
981 icmpsock6 = getrawsock(AF_INET6);
982#endif
983 }
984
985 if (icmpsock6 != INVALID_SOCKET)
986 {
987#ifdef ICMP6_FILTER // Windows doesn't support RFC 3542 API
988 /*
989 * XXX: We do this here for now, not in pxping.c, to avoid
990 * name clashes between lwIP and system headers.
991 */
992 struct icmp6_filter flt;
993 ICMP6_FILTER_SETBLOCKALL(&flt);
994
995 ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &flt);
996
997 ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &flt);
998 ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &flt);
999 ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &flt);
1000 ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &flt);
1001
1002 int status = setsockopt(icmpsock6, IPPROTO_ICMPV6, ICMP6_FILTER,
1003 &flt, sizeof(flt));
1004 if (status < 0)
1005 {
1006 perror("ICMP6_FILTER");
1007 }
1008#endif
1009 }
1010
1011 m_ProxyOptions.icmpsock6 = icmpsock6;
1012}
1013
1014
1015
1016/**
1017 * Adapter for the ListenerImpl template. It has to be a separate
1018 * object because ListenerImpl deletes it. Just a small wrapper that
1019 * delegates the real work back to VBoxNetLwipNAT.
1020 */
1021class VBoxNetLwipNAT::Listener::Adapter
1022{
1023 VBoxNetLwipNAT *m_pNAT;
1024public:
1025 Adapter() : m_pNAT(NULL) {}
1026 HRESULT init() { return init(NULL); }
1027 void uninit() { m_pNAT = NULL; }
1028
1029 HRESULT init(VBoxNetLwipNAT *pNAT)
1030 {
1031 m_pNAT = pNAT;
1032 return S_OK;
1033 }
1034
1035 HRESULT HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1036 {
1037 if (RT_LIKELY(m_pNAT != NULL))
1038 return m_pNAT->HandleEvent(aEventType, pEvent);
1039 else
1040 return S_OK;
1041 }
1042};
1043
1044
1045HRESULT
1046VBoxNetLwipNAT::Listener::init(VBoxNetLwipNAT *pNAT)
1047{
1048 HRESULT hrc;
1049
1050 hrc = m_pListenerImpl.createObject();
1051 if (FAILED(hrc))
1052 return hrc;
1053
1054 hrc = m_pListenerImpl->init(new Adapter(), pNAT);
1055 if (FAILED(hrc))
1056 {
1057 VBoxNetLwipNAT::reportComError(m_pListenerImpl, "init", hrc);
1058 return hrc;
1059 }
1060
1061 return hrc;
1062}
1063
1064
1065void
1066VBoxNetLwipNAT::Listener::uninit()
1067{
1068 unlisten();
1069 m_pListenerImpl.setNull();
1070}
1071
1072
1073/*
1074 * There's no base interface that exposes "eventSource" so fake it
1075 * with a template.
1076 */
1077template <typename IEventful>
1078HRESULT
1079VBoxNetLwipNAT::Listener::listen(const ComPtr<IEventful> &pEventful,
1080 const VBoxEventType_T aEvents[])
1081{
1082 HRESULT hrc;
1083
1084 if (m_pListenerImpl.isNull())
1085 return S_OK;
1086
1087 ComPtr<IEventSource> pEventSource;
1088 hrc = pEventful->COMGETTER(EventSource)(pEventSource.asOutParam());
1089 if (FAILED(hrc))
1090 {
1091 VBoxNetLwipNAT::reportComError(pEventful, "EventSource", hrc);
1092 return hrc;
1093 }
1094
1095 /* got a real interface, punt to the non-template code */
1096 hrc = doListen(pEventSource, aEvents);
1097 if (FAILED(hrc))
1098 return hrc;
1099
1100 return hrc;
1101}
1102
1103
1104HRESULT
1105VBoxNetLwipNAT::Listener::doListen(const ComPtr<IEventSource> &pEventSource,
1106 const VBoxEventType_T aEvents[])
1107{
1108 HRESULT hrc;
1109
1110 com::SafeArray<VBoxEventType_T> aInteresting;
1111 for (size_t i = 0; aEvents[i] != VBoxEventType_Invalid; ++i)
1112 aInteresting.push_back(aEvents[i]);
1113
1114 BOOL fActive = true;
1115 hrc = pEventSource->RegisterListener(m_pListenerImpl,
1116 ComSafeArrayAsInParam(aInteresting),
1117 fActive);
1118 if (FAILED(hrc))
1119 {
1120 VBoxNetLwipNAT::reportComError(m_pEventSource, "RegisterListener", hrc);
1121 return hrc;
1122 }
1123
1124 m_pEventSource = pEventSource;
1125 return hrc;
1126}
1127
1128
1129HRESULT
1130VBoxNetLwipNAT::Listener::unlisten()
1131{
1132 HRESULT hrc;
1133
1134 if (m_pEventSource.isNull())
1135 return S_OK;
1136
1137 const ComPtr<IEventSource> pEventSource = m_pEventSource;
1138 m_pEventSource.setNull();
1139
1140 hrc = pEventSource->UnregisterListener(m_pListenerImpl);
1141 if (FAILED(hrc))
1142 {
1143 VBoxNetLwipNAT::reportComError(pEventSource, "UnregisterListener", hrc);
1144 return hrc;
1145 }
1146
1147 return hrc;
1148}
1149
1150
1151
1152/**
1153 * Create and register API event listeners.
1154 */
1155int VBoxNetLwipNAT::initComEvents()
1156{
1157 /**
1158 * @todo r=uwe These events are reported on both IVirtualBox and
1159 * INATNetwork objects. We used to listen for them on our
1160 * network, but it was changed later to listen on vbox. Leave it
1161 * that way for now. Note that HandleEvent() has to do additional
1162 * check for them to ignore events for other networks.
1163 */
1164 static const VBoxEventType_T s_aNATNetEvents[] = {
1165 VBoxEventType_OnNATNetworkPortForward,
1166 VBoxEventType_OnNATNetworkSetting,
1167 VBoxEventType_Invalid
1168 };
1169 m_ListenerNATNet.init(this);
1170 m_ListenerNATNet.listen(virtualbox, s_aNATNetEvents); // sic!
1171
1172 static const VBoxEventType_T s_aVirtualBoxEvents[] = {
1173 VBoxEventType_OnHostNameResolutionConfigurationChange,
1174 VBoxEventType_OnNATNetworkStartStop,
1175 VBoxEventType_Invalid
1176 };
1177 m_ListenerVirtualBox.init(this);
1178 m_ListenerVirtualBox.listen(virtualbox, s_aVirtualBoxEvents);
1179
1180 static const VBoxEventType_T s_aVBoxClientEvents[] = {
1181 VBoxEventType_OnVBoxSVCAvailabilityChanged,
1182 VBoxEventType_Invalid
1183 };
1184 m_ListenerVBoxClient.init(this);
1185 m_ListenerVBoxClient.listen(virtualboxClient, s_aVBoxClientEvents);
1186
1187 return VINF_SUCCESS;
1188}
1189
1190
1191/**
1192 * Perform lwIP initialization on the lwIP "tcpip" thread.
1193 *
1194 * The lwIP thread was created in init() and this function is run
1195 * before the main lwIP loop is started. It is responsible for
1196 * setting up lwIP state, configuring interface(s), etc.
1197 a*/
1198/*static*/
1199DECLCALLBACK(void) VBoxNetLwipNAT::onLwipTcpIpInit(void *arg)
1200{
1201 AssertPtrReturnVoid(arg);
1202 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(arg);
1203
1204 HRESULT hrc = com::Initialize();
1205 AssertComRCReturnVoid(hrc);
1206
1207 proxy_arp_hook = pxremap_proxy_arp;
1208 proxy_ip4_divert_hook = pxremap_ip4_divert;
1209
1210 proxy_na_hook = pxremap_proxy_na;
1211 proxy_ip6_divert_hook = pxremap_ip6_divert;
1212
1213 netif *pNetif = netif_add(&self->m_LwipNetIf /* Lwip Interface */,
1214 &self->m_ProxyOptions.ipv4_addr, /* IP address*/
1215 &self->m_ProxyOptions.ipv4_mask, /* Network mask */
1216 &self->m_ProxyOptions.ipv4_addr, /* XXX: Gateway address */
1217 self /* state */,
1218 VBoxNetLwipNAT::netifInit /* netif_init_fn */,
1219 tcpip_input /* netif_input_fn */);
1220
1221 AssertPtrReturnVoid(pNetif);
1222
1223 LogRel(("netif %c%c%d: mac %RTmac\n",
1224 pNetif->name[0], pNetif->name[1], pNetif->num,
1225 pNetif->hwaddr));
1226 LogRel(("netif %c%c%d: inet %RTnaipv4 netmask %RTnaipv4\n",
1227 pNetif->name[0], pNetif->name[1], pNetif->num,
1228 pNetif->ip_addr, pNetif->netmask));
1229 for (int i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
1230 if (!ip6_addr_isinvalid(netif_ip6_addr_state(pNetif, i))) {
1231 LogRel(("netif %c%c%d: inet6 %RTnaipv6\n",
1232 pNetif->name[0], pNetif->name[1], pNetif->num,
1233 netif_ip6_addr(pNetif, i)));
1234 }
1235 }
1236
1237 netif_set_up(pNetif);
1238 netif_set_link_up(pNetif);
1239
1240 if (self->m_ProxyOptions.ipv6_enabled) {
1241 /*
1242 * XXX: lwIP currently only ever calls mld6_joingroup() in
1243 * nd6_tmr() for fresh tentative addresses, which is a wrong place
1244 * to do it - but I'm not keen on fixing this properly for now
1245 * (with correct handling of interface up and down transitions,
1246 * etc). So stick it here as a kludge.
1247 */
1248 for (int i = 0; i <= 1; ++i) {
1249 ip6_addr_t *paddr = netif_ip6_addr(pNetif, i);
1250
1251 ip6_addr_t solicited_node_multicast_address;
1252 ip6_addr_set_solicitednode(&solicited_node_multicast_address,
1253 paddr->addr[3]);
1254 mld6_joingroup(paddr, &solicited_node_multicast_address);
1255 }
1256
1257 /*
1258 * XXX: We must join the solicited-node multicast for the
1259 * addresses we do IPv6 NA-proxy for. We map IPv6 loopback to
1260 * proxy address + 1. We only need the low 24 bits, and those are
1261 * fixed.
1262 */
1263 {
1264 ip6_addr_t solicited_node_multicast_address;
1265
1266 ip6_addr_set_solicitednode(&solicited_node_multicast_address,
1267 /* last 24 bits of the address */
1268 PP_HTONL(0x00000002));
1269 mld6_netif_joingroup(pNetif, &solicited_node_multicast_address);
1270 }
1271 }
1272
1273 proxy_init(&self->m_LwipNetIf, &self->m_ProxyOptions);
1274
1275 natServiceProcessRegisteredPf(self->m_vecPortForwardRule4);
1276 natServiceProcessRegisteredPf(self->m_vecPortForwardRule6);
1277}
1278
1279
1280/**
1281 * lwIP's callback to configure the interface.
1282 *
1283 * Called from onLwipTcpIpInit() via netif_add(). Called after the
1284 * initerface is mostly initialized, and its IPv4 address is already
1285 * configured. Here we still need to configure the MAC address and
1286 * IPv6 addresses. It's best to consult the source of netif_add() for
1287 * the exact details.
1288 */
1289/* static */ DECLCALLBACK(err_t)
1290VBoxNetLwipNAT::netifInit(netif *pNetif) RT_NOTHROW_DEF
1291{
1292 err_t rcLwip = ERR_OK;
1293
1294 AssertPtrReturn(pNetif, ERR_ARG);
1295
1296 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(pNetif->state);
1297 AssertPtrReturn(self, ERR_ARG);
1298
1299 LogFlowFunc(("ENTER: pNetif[%c%c%d]\n", pNetif->name[0], pNetif->name[1], pNetif->num));
1300 /* validity */
1301 AssertReturn( pNetif->name[0] == 'N'
1302 && pNetif->name[1] == 'T', ERR_ARG);
1303
1304
1305 pNetif->hwaddr_len = sizeof(RTMAC);
1306 memcpy(pNetif->hwaddr, &self->m_MacAddress, sizeof(RTMAC));
1307
1308 self->m_u16Mtu = 1500; // XXX: FIXME
1309 pNetif->mtu = self->m_u16Mtu;
1310
1311 pNetif->flags = NETIF_FLAG_BROADCAST
1312 | NETIF_FLAG_ETHARP /* Don't bother driver with ARP and let Lwip resolve ARP handling */
1313 | NETIF_FLAG_ETHERNET; /* Lwip works with ethernet too */
1314
1315 pNetif->linkoutput = netifLinkoutput; /* ether-level-pipe */
1316 pNetif->output = etharp_output; /* ip-pipe */
1317
1318 if (self->m_ProxyOptions.ipv6_enabled) {
1319 pNetif->output_ip6 = ethip6_output;
1320
1321 /* IPv6 link-local address in slot 0 */
1322 netif_create_ip6_linklocal_address(pNetif, /* :from_mac_48bit */ 1);
1323 netif_ip6_addr_set_state(pNetif, 0, IP6_ADDR_PREFERRED); // skip DAD
1324
1325 /* INATNetwork::IPv6Prefix in slot 1 */
1326 memcpy(netif_ip6_addr(pNetif, 1),
1327 &self->m_ProxyOptions.ipv6_addr, sizeof(ip6_addr_t));
1328 netif_ip6_addr_set_state(pNetif, 1, IP6_ADDR_PREFERRED);
1329
1330#if LWIP_IPV6_SEND_ROUTER_SOLICIT
1331 pNetif->rs_count = 0;
1332#endif
1333 }
1334
1335 LogFlowFunc(("LEAVE: %d\n", rcLwip));
1336 return rcLwip;
1337}
1338
1339
1340/**
1341 * Run the pumps.
1342 *
1343 * Spawn the intnet pump thread that gets packets from the intnet and
1344 * feeds them to lwIP. Enter COM event loop here, on the main thread.
1345 */
1346int
1347VBoxNetLwipNAT::run()
1348{
1349 int rc;
1350
1351 AssertReturn(m_hThrRecv == NIL_RTTHREAD, VERR_INVALID_STATE);
1352
1353 /* spawn the lwIP tcpip thread */
1354 vboxLwipCoreInitialize(VBoxNetLwipNAT::onLwipTcpIpInit, this);
1355
1356 /* spawn intnet input pump */
1357 rc = RTThreadCreate(&m_hThrRecv,
1358 VBoxNetLwipNAT::receiveThread, this,
1359 0, /* :cbStack */
1360 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1361 "RECV");
1362 AssertRCReturn(rc, rc);
1363
1364 /* main thread will run the API event queue pump */
1365 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1366 if (pQueue == NULL)
1367 {
1368 LogRel(("run: getMainEventQueue() == NULL\n"));
1369 return VERR_GENERAL_FAILURE;
1370 }
1371
1372 /* dispatch API events to our listeners */
1373 for (;;)
1374 {
1375 rc = pQueue->processEventQueue(RT_INDEFINITE_WAIT);
1376 if (rc == VERR_INTERRUPTED)
1377 {
1378 LogRel(("run: shutdown\n"));
1379 break;
1380 }
1381 else if (rc != VINF_SUCCESS)
1382 {
1383 /* note any unexpected rc */
1384 LogRel(("run: processEventQueue: %Rrc\n", rc));
1385 }
1386 }
1387
1388 /*
1389 * We are out of the event loop, so we were told to shut down.
1390 * Tell other threads to wrap up.
1391 */
1392
1393 /* tell the intnet input pump to terminate */
1394 m_IntNetIf.ifAbort();
1395
1396 /* tell the lwIP tcpip thread to terminate */
1397 vboxLwipCoreFinalize(VBoxNetLwipNAT::onLwipTcpIpFini, this);
1398
1399 rc = RTThreadWait(m_hThrRecv, 5000, NULL);
1400 m_hThrRecv = NIL_RTTHREAD;
1401
1402 return VINF_SUCCESS;
1403}
1404
1405
1406void
1407VBoxNetLwipNAT::shutdown()
1408{
1409 int rc;
1410
1411 com::NativeEventQueue *pQueue = com::NativeEventQueue::getMainEventQueue();
1412 if (pQueue == NULL)
1413 {
1414 LogRel(("shutdown: getMainEventQueue() == NULL\n"));
1415 return;
1416 }
1417
1418 /* unregister listeners */
1419 m_ListenerNATNet.unlisten();
1420 m_ListenerVirtualBox.unlisten();
1421 m_ListenerVBoxClient.unlisten();
1422
1423 /* tell the event loop in run() to stop */
1424 rc = pQueue->interruptEventQueueProcessing();
1425 if (RT_FAILURE(rc))
1426 LogRel(("shutdown: interruptEventQueueProcessing: %Rrc\n", rc));
1427}
1428
1429
1430/**
1431 * Run finalization on the lwIP "tcpip" thread.
1432 */
1433/* static */
1434DECLCALLBACK(void) VBoxNetLwipNAT::onLwipTcpIpFini(void *arg)
1435{
1436 AssertPtrReturnVoid(arg);
1437 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(arg);
1438
1439 /* XXX: proxy finalization */
1440 netif_set_link_down(&self->m_LwipNetIf);
1441 netif_set_down(&self->m_LwipNetIf);
1442 netif_remove(&self->m_LwipNetIf);
1443}
1444
1445
1446/**
1447 * @note: this work on Event thread.
1448 */
1449HRESULT VBoxNetLwipNAT::HandleEvent(VBoxEventType_T aEventType, IEvent *pEvent)
1450{
1451 HRESULT hrc = S_OK;
1452 switch (aEventType)
1453 {
1454 case VBoxEventType_OnNATNetworkSetting:
1455 {
1456 ComPtr<INATNetworkSettingEvent> pSettingsEvent(pEvent);
1457
1458 com::Bstr networkName;
1459 hrc = pSettingsEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1460 AssertComRCReturn(hrc, hrc);
1461 if (networkName != m_strNetworkName)
1462 break; /* change not for our network */
1463
1464 // XXX: only handle IPv6 default route for now
1465 if (!m_ProxyOptions.ipv6_enabled)
1466 break;
1467
1468 BOOL fIPv6DefaultRoute = FALSE;
1469 hrc = pSettingsEvent->COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(&fIPv6DefaultRoute);
1470 AssertComRCReturn(hrc, hrc);
1471
1472 if (m_ProxyOptions.ipv6_defroute == fIPv6DefaultRoute)
1473 break;
1474
1475 m_ProxyOptions.ipv6_defroute = fIPv6DefaultRoute;
1476 tcpip_callback_with_block(proxy_rtadvd_do_quick, &m_LwipNetIf, 0);
1477 break;
1478 }
1479
1480 case VBoxEventType_OnNATNetworkPortForward:
1481 {
1482 ComPtr<INATNetworkPortForwardEvent> pForwardEvent = pEvent;
1483
1484 com::Bstr networkName;
1485 hrc = pForwardEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1486 AssertComRCReturn(hrc, hrc);
1487 if (networkName != m_strNetworkName)
1488 break; /* change not for our network */
1489
1490 BOOL fCreateFW;
1491 hrc = pForwardEvent->COMGETTER(Create)(&fCreateFW);
1492 AssertComRCReturn(hrc, hrc);
1493
1494 BOOL fIPv6FW;
1495 hrc = pForwardEvent->COMGETTER(Ipv6)(&fIPv6FW);
1496 AssertComRCReturn(hrc, hrc);
1497
1498 com::Bstr name;
1499 hrc = pForwardEvent->COMGETTER(Name)(name.asOutParam());
1500 AssertComRCReturn(hrc, hrc);
1501
1502 NATProtocol_T proto = NATProtocol_TCP;
1503 hrc = pForwardEvent->COMGETTER(Proto)(&proto);
1504 AssertComRCReturn(hrc, hrc);
1505
1506 com::Bstr strHostAddr;
1507 hrc = pForwardEvent->COMGETTER(HostIp)(strHostAddr.asOutParam());
1508 AssertComRCReturn(hrc, hrc);
1509
1510 LONG lHostPort;
1511 hrc = pForwardEvent->COMGETTER(HostPort)(&lHostPort);
1512 AssertComRCReturn(hrc, hrc);
1513
1514 com::Bstr strGuestAddr;
1515 hrc = pForwardEvent->COMGETTER(GuestIp)(strGuestAddr.asOutParam());
1516 AssertComRCReturn(hrc, hrc);
1517
1518 LONG lGuestPort;
1519 hrc = pForwardEvent->COMGETTER(GuestPort)(&lGuestPort);
1520 AssertComRCReturn(hrc, hrc);
1521
1522 VECNATSERVICEPF& rules = fIPv6FW ? m_vecPortForwardRule6
1523 : m_vecPortForwardRule4;
1524
1525 NATSERVICEPORTFORWARDRULE r;
1526 RT_ZERO(r);
1527
1528 r.Pfr.fPfrIPv6 = fIPv6FW;
1529
1530 switch (proto)
1531 {
1532 case NATProtocol_TCP:
1533 r.Pfr.iPfrProto = IPPROTO_TCP;
1534 break;
1535 case NATProtocol_UDP:
1536 r.Pfr.iPfrProto = IPPROTO_UDP;
1537 break;
1538
1539 default:
1540 LogRel(("Event: %s %s port-forwarding rule \"%s\": invalid protocol %d\n",
1541 fCreateFW ? "Add" : "Remove",
1542 fIPv6FW ? "IPv6" : "IPv4",
1543 com::Utf8Str(name).c_str(),
1544 (int)proto));
1545 goto port_forward_done;
1546 }
1547
1548 LogRel(("Event: %s %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1549 fCreateFW ? "Add" : "Remove",
1550 fIPv6FW ? "IPv6" : "IPv4",
1551 com::Utf8Str(name).c_str(),
1552 proto == NATProtocol_TCP ? "TCP" : "UDP",
1553 /* from */
1554 fIPv6FW ? "[" : "",
1555 com::Utf8Str(strHostAddr).c_str(),
1556 fIPv6FW ? "]" : "",
1557 lHostPort,
1558 /* to */
1559 fIPv6FW ? "[" : "",
1560 com::Utf8Str(strGuestAddr).c_str(),
1561 fIPv6FW ? "]" : "",
1562 lGuestPort));
1563
1564 if (name.length() > sizeof(r.Pfr.szPfrName))
1565 {
1566 hrc = E_INVALIDARG;
1567 goto port_forward_done;
1568 }
1569
1570 RTStrPrintf(r.Pfr.szPfrName, sizeof(r.Pfr.szPfrName),
1571 "%s", com::Utf8Str(name).c_str());
1572
1573 RTStrPrintf(r.Pfr.szPfrHostAddr, sizeof(r.Pfr.szPfrHostAddr),
1574 "%s", com::Utf8Str(strHostAddr).c_str());
1575
1576 /* XXX: limits should be checked */
1577 r.Pfr.u16PfrHostPort = (uint16_t)lHostPort;
1578
1579 RTStrPrintf(r.Pfr.szPfrGuestAddr, sizeof(r.Pfr.szPfrGuestAddr),
1580 "%s", com::Utf8Str(strGuestAddr).c_str());
1581
1582 /* XXX: limits should be checked */
1583 r.Pfr.u16PfrGuestPort = (uint16_t)lGuestPort;
1584
1585 if (fCreateFW) /* Addition */
1586 {
1587 int rc = natServicePfRegister(r);
1588 if (RT_SUCCESS(rc))
1589 rules.push_back(r);
1590 }
1591 else /* Deletion */
1592 {
1593 ITERATORNATSERVICEPF it;
1594 for (it = rules.begin(); it != rules.end(); ++it)
1595 {
1596 /* compare */
1597 NATSERVICEPORTFORWARDRULE &natFw = *it;
1598 if ( natFw.Pfr.iPfrProto == r.Pfr.iPfrProto
1599 && natFw.Pfr.u16PfrHostPort == r.Pfr.u16PfrHostPort
1600 && strncmp(natFw.Pfr.szPfrHostAddr, r.Pfr.szPfrHostAddr, INET6_ADDRSTRLEN) == 0
1601 && natFw.Pfr.u16PfrGuestPort == r.Pfr.u16PfrGuestPort
1602 && strncmp(natFw.Pfr.szPfrGuestAddr, r.Pfr.szPfrGuestAddr, INET6_ADDRSTRLEN) == 0)
1603 {
1604 fwspec *pFwCopy = (fwspec *)RTMemDup(&natFw.FWSpec, sizeof(natFw.FWSpec));
1605 if (pFwCopy)
1606 {
1607 int status = portfwd_rule_del(pFwCopy);
1608 if (status == 0)
1609 rules.erase(it); /* (pFwCopy is owned by lwip thread now.) */
1610 else
1611 RTMemFree(pFwCopy);
1612 }
1613 break;
1614 }
1615 } /* loop over vector elements */
1616 } /* condition add or delete */
1617 port_forward_done:
1618 /* clean up strings */
1619 name.setNull();
1620 strHostAddr.setNull();
1621 strGuestAddr.setNull();
1622 break;
1623 }
1624
1625 case VBoxEventType_OnHostNameResolutionConfigurationChange:
1626 {
1627 const char **ppcszNameServers = getHostNameservers();
1628 err_t error;
1629
1630 error = tcpip_callback_with_block(pxdns_set_nameservers,
1631 ppcszNameServers,
1632 /* :block */ 0);
1633 if (error != ERR_OK && ppcszNameServers != NULL)
1634 RTMemFree(ppcszNameServers);
1635 break;
1636 }
1637
1638 case VBoxEventType_OnNATNetworkStartStop:
1639 {
1640 ComPtr <INATNetworkStartStopEvent> pStartStopEvent = pEvent;
1641
1642 com::Bstr networkName;
1643 hrc = pStartStopEvent->COMGETTER(NetworkName)(networkName.asOutParam());
1644 AssertComRCReturn(hrc, hrc);
1645 if (networkName != m_strNetworkName)
1646 break; /* change not for our network */
1647
1648 BOOL fStart = TRUE;
1649 hrc = pStartStopEvent->COMGETTER(StartEvent)(&fStart);
1650 AssertComRCReturn(hrc, hrc);
1651
1652 if (!fStart)
1653 shutdown();
1654 break;
1655 }
1656
1657 case VBoxEventType_OnVBoxSVCAvailabilityChanged:
1658 {
1659 LogRel(("VBoxSVC became unavailable, exiting.\n"));
1660 shutdown();
1661 break;
1662 }
1663
1664 default: break; /* Shut up MSC. */
1665 }
1666 return hrc;
1667}
1668
1669
1670/**
1671 * Read the list of host's resolvers via the API.
1672 *
1673 * Called during initialization and in response to the
1674 * VBoxEventType_OnHostNameResolutionConfigurationChange event.
1675 */
1676const char **VBoxNetLwipNAT::getHostNameservers()
1677{
1678 if (m_host.isNull())
1679 return NULL;
1680
1681 com::SafeArray<BSTR> aNameServers;
1682 HRESULT hrc = m_host->COMGETTER(NameServers)(ComSafeArrayAsOutParam(aNameServers));
1683 if (FAILED(hrc))
1684 return NULL;
1685
1686 const size_t cNameServers = aNameServers.size();
1687 if (cNameServers == 0)
1688 return NULL;
1689
1690 const char **ppcszNameServers =
1691 (const char **)RTMemAllocZ(sizeof(char *) * (cNameServers + 1));
1692 if (ppcszNameServers == NULL)
1693 return NULL;
1694
1695 size_t idxLast = 0;
1696 for (size_t i = 0; i < cNameServers; ++i)
1697 {
1698 com::Utf8Str strNameServer(aNameServers[i]);
1699 ppcszNameServers[idxLast] = RTStrDup(strNameServer.c_str());
1700 if (ppcszNameServers[idxLast] != NULL)
1701 ++idxLast;
1702 }
1703
1704 if (idxLast == 0)
1705 {
1706 RTMemFree(ppcszNameServers);
1707 return NULL;
1708 }
1709
1710 return ppcszNameServers;
1711}
1712
1713
1714/**
1715 * Fetch port-forwarding rules via the API.
1716 *
1717 * Reads the initial sets of rules from VBoxSVC. The rules will be
1718 * activated when all the initialization and plumbing is done. See
1719 * natServiceProcessRegisteredPf().
1720 */
1721int VBoxNetLwipNAT::fetchNatPortForwardRules(VECNATSERVICEPF &vec, bool fIsIPv6)
1722{
1723 HRESULT hrc;
1724
1725 com::SafeArray<BSTR> rules;
1726 if (fIsIPv6)
1727 hrc = m_net->COMGETTER(PortForwardRules6)(ComSafeArrayAsOutParam(rules));
1728 else
1729 hrc = m_net->COMGETTER(PortForwardRules4)(ComSafeArrayAsOutParam(rules));
1730 AssertComRCReturn(hrc, VERR_INTERNAL_ERROR);
1731
1732 NATSERVICEPORTFORWARDRULE Rule;
1733 for (size_t idxRules = 0; idxRules < rules.size(); ++idxRules)
1734 {
1735 Log(("%d-%s rule: %ls\n", idxRules, (fIsIPv6 ? "IPv6" : "IPv4"), rules[idxRules]));
1736 RT_ZERO(Rule);
1737
1738 int rc = netPfStrToPf(com::Utf8Str(rules[idxRules]).c_str(), fIsIPv6,
1739 &Rule.Pfr);
1740 if (RT_FAILURE(rc))
1741 continue;
1742
1743 vec.push_back(Rule);
1744 }
1745
1746 return VINF_SUCCESS;
1747}
1748
1749
1750/**
1751 * Activate the initial set of port-forwarding rules.
1752 *
1753 * Happens after lwIP and lwIP proxy is initialized, right before lwIP
1754 * thread starts processing messages.
1755 */
1756/* static */
1757int VBoxNetLwipNAT::natServiceProcessRegisteredPf(VECNATSERVICEPF& vecRules)
1758{
1759 ITERATORNATSERVICEPF it;
1760 for (it = vecRules.begin(); it != vecRules.end(); ++it)
1761 {
1762 NATSERVICEPORTFORWARDRULE &natPf = *it;
1763
1764 LogRel(("Loading %s port-forwarding rule \"%s\": %s %s%s%s:%d -> %s%s%s:%d\n",
1765 natPf.Pfr.fPfrIPv6 ? "IPv6" : "IPv4",
1766 natPf.Pfr.szPfrName,
1767 natPf.Pfr.iPfrProto == IPPROTO_TCP ? "TCP" : "UDP",
1768 /* from */
1769 natPf.Pfr.fPfrIPv6 ? "[" : "",
1770 natPf.Pfr.szPfrHostAddr,
1771 natPf.Pfr.fPfrIPv6 ? "]" : "",
1772 natPf.Pfr.u16PfrHostPort,
1773 /* to */
1774 natPf.Pfr.fPfrIPv6 ? "[" : "",
1775 natPf.Pfr.szPfrGuestAddr,
1776 natPf.Pfr.fPfrIPv6 ? "]" : "",
1777 natPf.Pfr.u16PfrGuestPort));
1778
1779 natServicePfRegister(natPf);
1780 }
1781
1782 return VINF_SUCCESS;
1783}
1784
1785
1786/**
1787 * Activate a single port-forwarding rule.
1788 *
1789 * This is used both when we activate all the initial rules on startup
1790 * and when port-forwarding rules are changed and we are notified via
1791 * an API event.
1792 */
1793/* static */
1794int VBoxNetLwipNAT::natServicePfRegister(NATSERVICEPORTFORWARDRULE &natPf)
1795{
1796 int lrc;
1797
1798 int sockFamily = (natPf.Pfr.fPfrIPv6 ? PF_INET6 : PF_INET);
1799 int socketSpec;
1800 switch(natPf.Pfr.iPfrProto)
1801 {
1802 case IPPROTO_TCP:
1803 socketSpec = SOCK_STREAM;
1804 break;
1805 case IPPROTO_UDP:
1806 socketSpec = SOCK_DGRAM;
1807 break;
1808 default:
1809 return VERR_IGNORED;
1810 }
1811
1812 const char *pszHostAddr = natPf.Pfr.szPfrHostAddr;
1813 if (pszHostAddr[0] == '\0')
1814 {
1815 if (sockFamily == PF_INET)
1816 pszHostAddr = "0.0.0.0";
1817 else
1818 pszHostAddr = "::";
1819 }
1820
1821 lrc = fwspec_set(&natPf.FWSpec,
1822 sockFamily,
1823 socketSpec,
1824 pszHostAddr,
1825 natPf.Pfr.u16PfrHostPort,
1826 natPf.Pfr.szPfrGuestAddr,
1827 natPf.Pfr.u16PfrGuestPort);
1828 if (lrc != 0)
1829 return VERR_IGNORED;
1830
1831 fwspec *pFwCopy = (fwspec *)RTMemDup(&natPf.FWSpec, sizeof(natPf.FWSpec));
1832 if (pFwCopy)
1833 {
1834 lrc = portfwd_rule_add(pFwCopy);
1835 if (lrc == 0)
1836 return VINF_SUCCESS; /* (pFwCopy is owned by lwip thread now.) */
1837 RTMemFree(pFwCopy);
1838 }
1839 else
1840 LogRel(("Unable to allocate memory for %s rule \"%s\"\n",
1841 natPf.Pfr.fPfrIPv6 ? "IPv6" : "IPv4",
1842 natPf.Pfr.szPfrName));
1843 return VERR_IGNORED;
1844}
1845
1846
1847/**
1848 * IntNetIf receive thread. Runs intnet pump with our processFrame()
1849 * as input callback.
1850 */
1851/* static */ DECLCALLBACK(int)
1852VBoxNetLwipNAT::receiveThread(RTTHREAD hThreadSelf, void *pvUser)
1853{
1854 HRESULT hrc;
1855 int rc;
1856
1857 RT_NOREF(hThreadSelf);
1858
1859 AssertReturn(pvUser != NULL, VERR_INVALID_PARAMETER);
1860 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(pvUser);
1861
1862 /* do we relaly need to init com on this thread? */
1863 hrc = com::Initialize();
1864 if (FAILED(hrc))
1865 return VERR_GENERAL_FAILURE;
1866
1867 rc = self->m_IntNetIf.setInputCallback(VBoxNetLwipNAT::processFrame, self);
1868 AssertRCReturn(rc, rc);
1869
1870 rc = self->m_IntNetIf.ifPump();
1871 if (rc == VERR_SEM_DESTROYED)
1872 return VINF_SUCCESS;
1873
1874 LogRel(("receiveThread: ifPump: unexpected %Rrc\n", rc));
1875 return VERR_INVALID_STATE;
1876}
1877
1878
1879/**
1880 * Process an incoming frame received from the intnet.
1881 */
1882/* static */ DECLCALLBACK(void)
1883VBoxNetLwipNAT::processFrame(void *pvUser, void *pvFrame, uint32_t cbFrame)
1884{
1885 AssertReturnVoid(pvFrame != NULL);
1886
1887 /* shouldn't happen, but if it does, don't even bother */
1888 if (RT_UNLIKELY(cbFrame < sizeof(RTNETETHERHDR)))
1889 return;
1890
1891 /* we expect normal ethernet frame including .1Q and FCS */
1892 if (cbFrame > 1522)
1893 return;
1894
1895 AssertReturnVoid(pvUser != NULL);
1896 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(pvUser);
1897
1898 struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)cbFrame + ETH_PAD_SIZE, PBUF_POOL);
1899 if (RT_UNLIKELY(p == NULL))
1900 return;
1901
1902 /*
1903 * The code below is inlined version of:
1904 *
1905 * pbuf_header(p, -ETH_PAD_SIZE); // hide padding
1906 * pbuf_take(p, pvFrame, cbFrame);
1907 * pbuf_header(p, ETH_PAD_SIZE); // reveal padding
1908 */
1909 struct pbuf *q = p;
1910 uint8_t *pu8Chunk = (uint8_t *)pvFrame;
1911 do {
1912 uint8_t *payload = (uint8_t *)q->payload;
1913 size_t len = q->len;
1914
1915#if ETH_PAD_SIZE
1916 if (RT_LIKELY(q == p)) // single pbuf is large enough
1917 {
1918 payload += ETH_PAD_SIZE;
1919 len -= ETH_PAD_SIZE;
1920 }
1921#endif
1922 memcpy(payload, pu8Chunk, len);
1923 pu8Chunk += len;
1924 q = q->next;
1925 } while (RT_UNLIKELY(q != NULL));
1926
1927 /* pass input to lwIP: netif input funcion tcpip_input() */
1928 self->m_LwipNetIf.input(p, &self->m_LwipNetIf);
1929}
1930
1931
1932/**
1933 * Send an outgoing frame from lwIP to intnet.
1934 */
1935/* static */ DECLCALLBACK(err_t)
1936VBoxNetLwipNAT::netifLinkoutput(netif *pNetif, pbuf *pPBuf) RT_NOTHROW_DEF
1937{
1938 int rc;
1939
1940 AssertPtrReturn(pNetif, ERR_ARG);
1941 AssertPtrReturn(pPBuf, ERR_ARG);
1942
1943 VBoxNetLwipNAT *self = static_cast<VBoxNetLwipNAT *>(pNetif->state);
1944 AssertPtrReturn(self, ERR_IF);
1945 AssertReturn(pNetif == &self->m_LwipNetIf, ERR_IF);
1946
1947 LogFlowFunc(("ENTER: pNetif[%c%c%d], pPbuf:%p\n",
1948 pNetif->name[0],
1949 pNetif->name[1],
1950 pNetif->num,
1951 pPBuf));
1952
1953 if (pPBuf->tot_len < sizeof(struct eth_hdr)) /* includes ETH_PAD_SIZE */
1954 return ERR_ARG;
1955
1956 size_t cbFrame = (size_t)pPBuf->tot_len - ETH_PAD_SIZE;
1957 IntNetIf::Frame frame;
1958 rc = self->m_IntNetIf.getOutputFrame(frame, cbFrame);
1959 if (RT_FAILURE(rc))
1960 return ERR_MEM;
1961
1962 pbuf_copy_partial(pPBuf, frame.pvFrame, (u16_t)cbFrame, ETH_PAD_SIZE);
1963 rc = self->m_IntNetIf.ifOutput(frame);
1964 if (RT_FAILURE(rc))
1965 return ERR_IF;
1966
1967 LogFlowFunc(("LEAVE: %d\n", ERR_OK));
1968 return ERR_OK;
1969}
1970
1971
1972/**
1973 * Retrieve network-specific extra data item.
1974 */
1975int VBoxNetLwipNAT::getExtraData(com::Utf8Str &strValueOut, const char *pcszKey)
1976{
1977 HRESULT hrc;
1978
1979 AssertReturn(!virtualbox.isNull(), E_FAIL);
1980 AssertReturn(m_strNetworkName.isNotEmpty(), E_FAIL);
1981 AssertReturn(pcszKey != NULL, E_FAIL);
1982 AssertReturn(*pcszKey != '\0', E_FAIL);
1983
1984 com::BstrFmt bstrKey("NAT/%s/%s", m_strNetworkName.c_str(), pcszKey);
1985 com::Bstr bstrValue;
1986 hrc = virtualbox->GetExtraData(bstrKey.raw(), bstrValue.asOutParam());
1987 if (FAILED(hrc))
1988 {
1989 reportComError(virtualbox, "GetExtraData", hrc);
1990 return VERR_GENERAL_FAILURE;
1991 }
1992
1993 strValueOut = bstrValue;
1994 return VINF_SUCCESS;
1995}
1996
1997
1998/* static */
1999HRESULT VBoxNetLwipNAT::reportComError(ComPtr<IUnknown> iface,
2000 const com::Utf8Str &strContext,
2001 HRESULT hrc)
2002{
2003 const com::ErrorInfo info(iface, COM_IIDOF(IUnknown));
2004 if (info.isFullAvailable() || info.isBasicAvailable())
2005 {
2006 reportErrorInfoList(info, strContext);
2007 }
2008 else
2009 {
2010 if (strContext.isNotEmpty())
2011 reportError("%s: %Rhra", strContext.c_str(), hrc);
2012 else
2013 reportError("%Rhra", hrc);
2014 }
2015
2016 return hrc;
2017}
2018
2019
2020/* static */
2021void VBoxNetLwipNAT::reportErrorInfoList(const com::ErrorInfo &info,
2022 const com::Utf8Str &strContext)
2023{
2024 if (strContext.isNotEmpty())
2025 reportError("%s", strContext.c_str());
2026
2027 bool fFirst = true;
2028 for (const com::ErrorInfo *pInfo = &info;
2029 pInfo != NULL;
2030 pInfo = pInfo->getNext())
2031 {
2032 if (fFirst)
2033 fFirst = false;
2034 else
2035 reportError("--------");
2036
2037 reportErrorInfo(*pInfo);
2038 }
2039}
2040
2041
2042/* static */
2043void VBoxNetLwipNAT::reportErrorInfo(const com::ErrorInfo &info)
2044{
2045#if defined (RT_OS_WIN)
2046 bool haveResultCode = info.isFullAvailable();
2047 bool haveComponent = true;
2048 bool haveInterfaceID = true;
2049#else /* !RT_OS_WIN */
2050 bool haveResultCode = true;
2051 bool haveComponent = info.isFullAvailable();
2052 bool haveInterfaceID = info.isFullAvailable();
2053#endif
2054 com::Utf8Str message;
2055 if (info.getText().isNotEmpty())
2056 message = info.getText();
2057
2058 const char *pcszDetails = "Details: ";
2059 const char *pcszComma = ", ";
2060 const char *pcszSeparator = pcszDetails;
2061
2062 if (haveResultCode)
2063 {
2064 message.appendPrintf("%s" "code %Rhrc (0x%RX32)",
2065 pcszSeparator, info.getResultCode(), info.getResultCode());
2066 pcszSeparator = pcszComma;
2067 }
2068
2069 if (haveComponent)
2070 {
2071 message.appendPrintf("%s" "component %ls",
2072 pcszSeparator, info.getComponent().raw());
2073 pcszSeparator = pcszComma;
2074 }
2075
2076 if (haveInterfaceID)
2077 {
2078 message.appendPrintf("%s" "interface %ls",
2079 pcszSeparator, info.getInterfaceName().raw());
2080 pcszSeparator = pcszComma;
2081 }
2082
2083 if (info.getCalleeName().isNotEmpty())
2084 {
2085 message.appendPrintf("%s" "callee %ls",
2086 pcszSeparator, info.getCalleeName().raw());
2087 pcszSeparator = pcszComma;
2088 }
2089
2090 reportError("%s", message.c_str());
2091}
2092
2093
2094/* static */
2095void VBoxNetLwipNAT::reportError(const char *a_pcszFormat, ...)
2096{
2097 va_list ap;
2098
2099 va_start(ap, a_pcszFormat);
2100 com::Utf8Str message(a_pcszFormat, ap);
2101 va_end(ap);
2102
2103 RTMsgError("%s", message.c_str());
2104 LogRel(("%s", message.c_str()));
2105}
2106
2107
2108
2109/**
2110 * Create release logger.
2111 *
2112 * The NAT network name is sanitized so that it can be used in a path
2113 * component. By default the release log is written to the file
2114 * ~/.VirtualBox/${netname}.log but its destiation and content can be
2115 * overridden with VBOXNET_${netname}_RELEASE_LOG family of
2116 * environment variables (also ..._DEST and ..._FLAGS).
2117 */
2118/* static */
2119int VBoxNetLwipNAT::initLog()
2120{
2121 size_t cch;
2122 int rc;
2123
2124 if (m_strNetworkName.isEmpty())
2125 return VERR_MISSING;
2126
2127 char szNetwork[RTPATH_MAX];
2128 rc = RTStrCopy(szNetwork, sizeof(szNetwork), m_strNetworkName.c_str());
2129 if (RT_FAILURE(rc))
2130 return rc;
2131
2132 // sanitize network name to be usable as a path component
2133 for (char *p = szNetwork; *p != '\0'; ++p)
2134 {
2135 if (RTPATH_IS_SEP(*p))
2136 *p = '_';
2137 }
2138
2139 const char *pcszLogFile = NULL;
2140 char szLogFile[RTPATH_MAX];
2141 if (m_strHome.isNotEmpty())
2142 {
2143 cch = RTStrPrintf(szLogFile, sizeof(szLogFile),
2144 "%s%c%s.log", m_strHome.c_str(), RTPATH_DELIMITER, szNetwork);
2145 if (cch < sizeof(szLogFile))
2146 pcszLogFile = szLogFile;
2147 }
2148
2149 // sanitize network name some more to be usable as environment variable
2150 for (char *p = szNetwork; *p != '\0'; ++p)
2151 {
2152 if (*p != '_'
2153 && (*p < '0' || '9' < *p)
2154 && (*p < 'a' || 'z' < *p)
2155 && (*p < 'A' || 'Z' < *p))
2156 {
2157 *p = '_';
2158 }
2159 }
2160
2161 char szEnvVarBase[128];
2162 const char *pcszEnvVarBase = szEnvVarBase;
2163 cch = RTStrPrintf(szEnvVarBase, sizeof(szEnvVarBase),
2164 "VBOXNET_%s_RELEASE_LOG", szNetwork);
2165 if (cch >= sizeof(szEnvVarBase))
2166 pcszEnvVarBase = NULL;
2167
2168 rc = com::VBoxLogRelCreate("NAT Network",
2169 pcszLogFile,
2170 RTLOGFLAGS_PREFIX_TIME_PROG,
2171 "all all.restrict -default.restrict",
2172 pcszEnvVarBase,
2173 RTLOGDEST_FILE,
2174 32768 /* cMaxEntriesPerGroup */,
2175 0 /* cHistory */,
2176 0 /* uHistoryFileTime */,
2177 0 /* uHistoryFileSize */,
2178 NULL /*pErrInfo*/);
2179
2180 /*
2181 * Provide immediate feedback if corresponding LogRel level is
2182 * enabled. It's frustrating when you chase some rare event and
2183 * discover you didn't actually have the corresponding log level
2184 * enabled because of a typo in the environment variable name or
2185 * its content.
2186 */
2187#define LOG_PING(_log) _log((#_log " enabled\n"))
2188 LOG_PING(LogRel2);
2189 LOG_PING(LogRel3);
2190 LOG_PING(LogRel4);
2191 LOG_PING(LogRel5);
2192 LOG_PING(LogRel6);
2193 LOG_PING(LogRel7);
2194 LOG_PING(LogRel8);
2195 LOG_PING(LogRel9);
2196 LOG_PING(LogRel10);
2197 LOG_PING(LogRel11);
2198 LOG_PING(LogRel12);
2199
2200 return rc;
2201}
2202
2203
2204/**
2205 * Entry point.
2206 */
2207extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
2208{
2209 LogFlowFuncEnter();
2210 NOREF(envp);
2211
2212#ifdef RT_OS_WINDOWS
2213 WSADATA WsaData = {0};
2214 int err = WSAStartup(MAKEWORD(2,2), &WsaData);
2215 if (err)
2216 {
2217 fprintf(stderr, "wsastartup: failed (%d)\n", err);
2218 return RTEXITCODE_INIT;
2219 }
2220#endif
2221
2222 VBoxNetLwipNAT NAT;
2223
2224 int rcExit = NAT.parseArgs(argc, argv);
2225 if (rcExit != RTEXITCODE_SUCCESS)
2226 {
2227 /* messages are already printed */
2228 return rcExit == RTEXITCODE_DONE ? RTEXITCODE_SUCCESS : rcExit;
2229 }
2230
2231 int rc = NAT.init();
2232 if (RT_FAILURE(rc))
2233 return RTEXITCODE_INIT;
2234
2235 NAT.run();
2236
2237 LogRel(("Terminating\n"));
2238 return RTEXITCODE_SUCCESS;
2239}
2240
2241
2242#ifndef VBOX_WITH_HARDENING
2243
2244int main(int argc, char **argv, char **envp)
2245{
2246 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_SUPLIB);
2247 if (RT_SUCCESS(rc))
2248 return TrustedMain(argc, argv, envp);
2249 return RTMsgInitFailure(rc);
2250}
2251
2252# if defined(RT_OS_WINDOWS)
2253
2254# if 0 /* Some copy and paste from DHCP that nobody explained why was diabled. */
2255static LRESULT CALLBACK WindowProc(HWND hwnd,
2256 UINT uMsg,
2257 WPARAM wParam,
2258 LPARAM lParam
2259)
2260{
2261 if(uMsg == WM_DESTROY)
2262 {
2263 PostQuitMessage(0);
2264 return 0;
2265 }
2266 return DefWindowProc (hwnd, uMsg, wParam, lParam);
2267}
2268
2269static LPCWSTR g_WndClassName = L"VBoxNetNatLwipClass";
2270
2271static DWORD WINAPI MsgThreadProc(__in LPVOID lpParameter)
2272{
2273 HWND hwnd = 0;
2274 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL);
2275 bool bExit = false;
2276
2277 /* Register the Window Class. */
2278 WNDCLASS wc;
2279 wc.style = 0;
2280 wc.lpfnWndProc = WindowProc;
2281 wc.cbClsExtra = 0;
2282 wc.cbWndExtra = sizeof(void *);
2283 wc.hInstance = hInstance;
2284 wc.hIcon = NULL;
2285 wc.hCursor = NULL;
2286 wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
2287 wc.lpszMenuName = NULL;
2288 wc.lpszClassName = g_WndClassName;
2289
2290 ATOM atomWindowClass = RegisterClass(&wc);
2291
2292 if (atomWindowClass != 0)
2293 {
2294 /* Create the window. */
2295 hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
2296 g_WndClassName, g_WndClassName, WS_POPUPWINDOW,
2297 -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
2298
2299 if (hwnd)
2300 {
2301 SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
2302 SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
2303
2304 MSG msg;
2305 while (GetMessage(&msg, NULL, 0, 0))
2306 {
2307 TranslateMessage(&msg);
2308 DispatchMessage(&msg);
2309 }
2310
2311 DestroyWindow (hwnd);
2312
2313 bExit = true;
2314 }
2315
2316 UnregisterClass (g_WndClassName, hInstance);
2317 }
2318
2319 if(bExit)
2320 {
2321 /* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
2322 exit(0);
2323 }
2324
2325 return 0;
2326}
2327# endif
2328
2329
2330/** (We don't want a console usually.) */
2331int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2332{
2333 RT_NOREF(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
2334# if 0 /* some copy and paste from DHCP that nobody explained why was diabled. */
2335 NOREF(hInstance); NOREF(hPrevInstance); NOREF(lpCmdLine); NOREF(nCmdShow);
2336
2337 HANDLE hThread = CreateThread(
2338 NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
2339 0, /*__in SIZE_T dwStackSize, */
2340 MsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
2341 NULL, /*__in_opt LPVOID lpParameter,*/
2342 0, /*__in DWORD dwCreationFlags,*/
2343 NULL /*__out_opt LPDWORD lpThreadId*/
2344 );
2345
2346 if(hThread != NULL)
2347 CloseHandle(hThread);
2348
2349# endif
2350 return main(__argc, __argv, environ);
2351}
2352# endif /* RT_OS_WINDOWS */
2353
2354#endif /* !VBOX_WITH_HARDENING */
Note: See TracBrowser for help on using the repository browser.

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