VirtualBox

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

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

SUP,IPRT,++: Adding SUPSECMAIN_FLAGS_DRIVERLESS_IEM_ALLOWED and SUPR3INIT_F_DRIVERLESS_NEM_FALLBACK to SUPLib and RTR3INIT_FLAGS_TRY_SUPLIB to RTR3Init*, the latter probably reflects the actual state there a lot better. Currently only the TRY_SUPLIB option works, the other two aren't really implemented in SUPLib yet. bugref:10138

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