VirtualBox

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

Last change on this file since 87637 was 87637, checked in by vboxsync, 4 years ago

NAT/Net: Get IPv6 address from the API instead of always constructing
one based on IPV4. bugref:8124, bugref:9929.

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

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