VirtualBox

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

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

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