VirtualBox

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

Last change on this file since 94068 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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