VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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