VirtualBox

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

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

/Config.kmk and many other places: Change VBOX_VENDOR to the official copyright holder text, needs follow-up changes and equivalent adjustments elsewhere.

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